This commit is contained in:
Maxence Lange 2019-04-09 18:03:36 -01:00
Родитель cbb906912b
Коммит 32b952d5af
52 изменённых файлов: 6021 добавлений и 227 удалений

2
.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,2 @@
\.idea/
vendor/

Просмотреть файл

@ -1,9 +1,16 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2017 Frank Karlitschek <frank@karlitschek.de>
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Frank Karlitschek <frank@karlitschek.de>
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
@ -21,5 +28,10 @@
*
*/
$app = new \OCA\Backup\AppInfo\Application();
$app->register();
namespace OCA\Backup\AppInfo;
require_once __DIR__ . '/autoload.php';

39
appinfo/autoload.php Normal file
Просмотреть файл

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\AppInfo;
$composerDir = __DIR__ . '/../vendor/';
if (is_dir($composerDir) && file_exists($composerDir . 'autoload.php')) {
require_once $composerDir . 'autoload.php';
}

Просмотреть файл

@ -1,19 +1,31 @@
<?xml version="1.0"?>
<info>
<info xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<id>backup</id>
<name>Backup</name>
<description><![CDATA[Allows admins to generate a backup of an Nextclod instance and restore it. Possible via occ or REST]]></description>
<licence>AGPL</licence>
<description><![CDATA[Allows admins to generate a backup of a Nextcloud instance and restore it. Possible via occ or REST]]></description>
<licence>agpl</licence>
<author mail="maxence@artificial-owl.com">Maxence Lange</author>
<author>Frank Karlitschek</author>
<version>1.0.0</version>
<version>0.1.0</version>
<namespace>Backup</namespace>
<category>tools</category>
<website>https://github.com/nextcloud/backup</website>
<bugs>https://github.com/nextcloud/backup/issues</bugs>
<repository type="git">https://github.com/nextcloud/backup.git</repository>
<screenshot>https://raw.githubusercontent.com/nextcloud/backup/master/img/screenshot.png</screenshot>
<dependencies>
<nextcloud min-version="12" max-version="12" />
<nextcloud min-version="16" max-version="17"/>
</dependencies>
<background-jobs>
<job>OCA\Backup\Cron\Backup</job>
</background-jobs>
<commands>
<command>OCA\Backup\Command\CreateCommand</command>
<command>OCA\Backup\Command\RestoreCommand</command>
<command>OCA\Backup\Command\Create</command>
<command>OCA\Backup\Command\Listing</command>
<command>OCA\Backup\Command\Details</command>
<command>OCA\Backup\Command\Restore</command>
</commands>
</info>

311
backup.php Normal file
Просмотреть файл

@ -0,0 +1,311 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup;
use Composer\Autoload\ClassLoader;
use daita\MySmallPhpTools\DI\DIContainer;
use daita\MySmallPhpTools\Exceptions\DependencyInjectionException;
use Exception;
use OC\Config;
use OC\Logger;
use OCA\Backup\Model\Backup;
use OCA\Backup\Service\BackupService;
use OCA\Backup\Service\CliService;
use OCP\IConfig;
use OCP\ILogger;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use ZipArchive;
/**
* extracting app files
*/
if (!extractAppFiles()) {
exit();
}
/**
* init few stuff.
*/
$verbose = false;
if (in_array('-v', $argv) || in_array('--verbose', $argv)) {
$verbose = true;
}
/**
* loading libs.
*/
if ($verbose) {
echo 'Loading libraries.' . "\n";
}
$classes = [
'Service\ArchiveService',
'Service\BackupService',
'Service\CliService',
'Service\ConfigService',
'Service\EncryptService',
'Service\FilesService',
'Service\MiscService',
'Model\ArchiveFile',
'Model\Backup',
'Model\BackupArchive',
'Model\BackupChunk',
'Model\BackupOptions',
'Model\Error',
'Model\RemoteStorage',
'ISqlDump',
'SqlDump\SqlDumpMySQL',
'Exceptions\ArchiveCreateException',
'Exceptions\ArchiveNotFoundException',
'Exceptions\BackupFolderException',
'Exceptions\BackupScriptNotFoundException',
'Exceptions\VersionException',
'Exceptions\EncryptionKeyException',
'Exceptions\ArchiveDeleteException',
'Exceptions\BackupAppCopyException',
'Exceptions\BackupNotFoundException',
];
$mockup = [
'OCP\IConfig',
'OC\Config',
'OCP\ILogger',
'OC\Logger'
];
loadClasses($classes, $mockup);
/**
* generate Container.
*/
if ($verbose) {
echo 'Generating Container.' . "\n";
}
$container = new DIContainer();
$container->registerInterface(IConfig::class, Config::class);
$container->registerInterface(ILogger::class, Logger::class);
/**
* init Services.
*/
if ($verbose) {
echo 'Loading Services.' . "\n";
}
try {
/**
* @var CliService $cliService
*/
$cliService = $container->query(CliService::class);
} catch (DependencyInjectionException $e) {
echo $e->getMessage() . "\n";
exit();
}
if ($verbose) {
echo 'App is ready.' . "\n";
}
/**
* init input/output
*/
$inputDefinition = generateInputDefinition();
$input = new ArgvInput($argv, $inputDefinition);
$output = new ConsoleOutput();
$output = $output->section();
$cliService->init($input, $output);
if ($verbose) {
$output->writeln('Switching to <info>better</info> console output!');
$output->writeln('');
}
/**
* parsing backup.json
*/
if ($verbose) {
echo 'Parsing backup.json.' . "\n";
}
$json = file_get_contents(BackupService::SUMMARY_FILE);
$backup = new Backup();
$backup->import(json_decode($json, true));
$backup->setLocal(true);
$backup->setEncryptionKey($input->getOption('key'));
$options = $backup->getOptions();
$options->setNewRoot($input->getOption('root'));
$options->setPath($input->getOption('path'));
$options->setSearch($input->getOption('search'));
$options->setConfigRaw($input->getOption('config'));
$options->setChunk($input->getOption('chunk'));
$options->setAll($input->getOption('all'));
$options->setArchive($input->getOption('archive'));
$options->setFixDataDir($input->getOption('fix-datadirectory'));
$cliService->displayBackupResume($backup);
/**
* let's start based on Options
*/
try {
switch ($input->getArgument('action')) {
case 'details':
$cliService->displayBackupDetails($backup);
break;
case 'restore':
$cliService->displayBackupRestore($backup);
break;
case 'files':
$cliService->displayFilesList($backup);
break;
default:
$output->writeln('details/restore/files');
break;
}
} catch (Exception $e) {
echo "\n" . $e->getMessage() . "\n\n";
exit();
}
/**
* The End.
*/
/**
* @return bool
*/
function extractAppFiles(): bool {
if (is_dir(__DIR__ . '/app/')) {
return true;
}
echo 'Extracting Backup App files (using PHP/ZipArchive) ' . "\n";
try {
$zip = new ZipArchive();
if (($err = $zip->open('app.zip')) !== true) {
throw new Exception('failed with error ' . $err);
}
$zip->extractTo('./');
$zip->close();
return true;
} catch (Exception $e) {
echo $e->getMessage() . "\n";
echo 'Extracting Backup App files (using unzip command) ' . "\n";
system('unzip app.zip');
return true;
}
}
/**
* @param array $classes
* @param array $mockups
*
* @return ClassLoader
*/
function loadClasses(array $classes, array $mockups): ClassLoader {
$r = 'OCA\Backup\\';
$p = __DIR__ . '/app/lib/';
$map = [];
foreach ($classes as $class) {
$classPath = str_replace('\\', '/', $class);
$map[$r . $class] = $p . $classPath . '.php';
}
foreach ($mockups as $class) {
$classPath = str_replace('\\', '/', $class);
$map[$class] = $p . 'Mockup/' . $classPath . '.php';
}
$loader = require 'app/vendor/autoload.php';
$loader->addClassMap($map);
$loader->register();
return $loader;
}
/**
* @return InputDefinition
*/
function generateInputDefinition(): InputDefinition {
$input = new InputDefinition();
$input->addOption(new InputOption('verbose', 'v'));
$input->addOption(new InputOption('full-install'));
$input->addOption(new InputOption('check'));
$input->addOption(new InputOption('key', 'k', InputOption::VALUE_REQUIRED, '', ''));
$input->addOption(new InputOption('root', '', InputOption::VALUE_REQUIRED, '', ''));
$input->addOption(new InputOption('search', '', InputOption::VALUE_REQUIRED, '', ''));
$input->addOption(new InputOption('config', '', InputOption::VALUE_REQUIRED, '', '{}'));
$input->addOption(new InputOption('path', '', InputOption::VALUE_REQUIRED, '', ''));
$input->addOption(new InputOption('chunk', '', InputOption::VALUE_REQUIRED, '', ''));
$input->addOption(new InputOption('archive', '', InputOption::VALUE_REQUIRED, '', ''));
$input->addOption(new InputOption('dir', '', InputOption::VALUE_REQUIRED, '', ''));
$input->addOption(new InputOption('fix-datadirectory'));
$input->addOption(new InputOption('all'));
$input->addArgument(new InputArgument('action'));
$input->addArgument(new InputArgument('search'));
return $input;
}

23
composer.json Normal file
Просмотреть файл

@ -0,0 +1,23 @@
{
"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"
}
}

23
composer.json~ Normal file
Просмотреть файл

@ -0,0 +1,23 @@
{
"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",
}
}

409
composer.lock сгенерированный Normal file
Просмотреть файл

@ -0,0 +1,409 @@
{
"_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",
"This file is @generated automatically"
],
"content-hash": "b6c5df6ef9c263971da6fe6265a51a5f",
"packages": [
{
"name": "daita/my-small-php-tools",
"version": "dev-master",
"source": {
"type": "git",
"url": "https://github.com/daita/my-small-php-tools.git",
"reference": "e2ac095a0148fec7aaafe5dc9edc20d65c2d1f14"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/e2ac095a0148fec7aaafe5dc9edc20d65c2d1f14",
"reference": "e2ac095a0148fec7aaafe5dc9edc20d65c2d1f14",
"shasum": ""
},
"require": {
"php": "^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"daita\\MySmallPhpTools\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"AGPL-3.0-or-later"
],
"authors": [
{
"name": "Maxence Lange",
"email": "maxence@artificial-owl.com"
}
],
"description": "My small PHP Tools",
"time": "2019-03-29T18:48:14+00:00"
},
{
"name": "ifsnop/mysqldump-php",
"version": "v2.0",
"source": {
"type": "git",
"url": "https://github.com/ifsnop/mysqldump-php.git",
"reference": "2f850d6c3ea6d55ca2cb7b37bb568264264f728e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ifsnop/mysqldump-php/zipball/2f850d6c3ea6d55ca2cb7b37bb568264264f728e",
"reference": "2f850d6c3ea6d55ca2cb7b37bb568264264f728e",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "3.7.*",
"squizlabs/php_codesniffer": "1.*"
},
"type": "library",
"autoload": {
"psr-4": {
"Ifsnop\\": "src/Ifsnop/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Diego Torres",
"homepage": "https://github.com/ifsnop",
"role": "Developer"
}
],
"description": "This is a php version of linux's mysqldump in terminal \"$ mysqldump -u username -p...\"",
"homepage": "https://github.com/ifsnop/mysqldump-php",
"keywords": [
"backup",
"database",
"dump",
"export",
"mysql",
"mysqldump",
"pdo",
"sqlite"
],
"time": "2015-09-10T19:58:38+00:00"
},
{
"name": "pimple/pimple",
"version": "v3.2.3",
"source": {
"type": "git",
"url": "https://github.com/silexphp/Pimple.git",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/silexphp/Pimple/zipball/9e403941ef9d65d20cba7d54e29fe906db42cf32",
"reference": "9e403941ef9d65d20cba7d54e29fe906db42cf32",
"shasum": ""
},
"require": {
"php": ">=5.3.0",
"psr/container": "^1.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Pimple": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
}
],
"description": "Pimple, a simple Dependency Injection Container",
"homepage": "http://pimple.sensiolabs.org",
"keywords": [
"container",
"dependency injection"
],
"time": "2018-01-21T07:42:36+00:00"
},
{
"name": "psr/container",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "http://www.php-fig.org/"
}
],
"description": "Common Container Interface (PHP FIG PSR-11)",
"homepage": "https://github.com/php-fig/container",
"keywords": [
"PSR-11",
"container",
"container-interface",
"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"
},
"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"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"daita/my-small-php-tools": 20
},
"prefer-stable": false,
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"platform-overrides": {
"php": "7.2.0"
}
}

Просмотреть файл

@ -1,9 +1,16 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2017 Frank Karlitschek <frank@karlitschek.de>
* Nextcloud - Backup
*
* @author Frank karlitschek <frank@karlitschek.de>
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Frank Karlitschek <frank@karlitschek.de>
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
@ -21,18 +28,32 @@
*
*/
namespace OCA\Backup\AppInfo;
use OCP\AppFramework\App;
/**
* Class Application
*
* @package OCA\Backup\AppInfo
*/
class Application extends App {
const APP_ID = 'backup';
public function __construct() {
parent::__construct(self::APP_ID);
}
public function register() {
/**
* Application constructor.
*
* @param array $params
*/
public function __construct(array $params = []) {
parent::__construct(self::APP_ID, $params);
}
}

Просмотреть файл

@ -23,13 +23,6 @@
namespace OCA\Backup\Backup;
use OCA\Backup\AppInfo\Application;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IRequest;
class Create {
/** @var path */

Просмотреть файл

@ -23,13 +23,6 @@
namespace OCA\Backup\Backup;
use OCA\Backup\AppInfo\Application;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IRequest;
class Restore {
/** @var path */

98
lib/Command/Create.php Executable file
Просмотреть файл

@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Frank Karlitschek <frank@karlitschek.de>
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Command;
use OC\Core\Command\Base;
use OCA\Backup\Service\BackupService;
use OCA\Backup\Service\MiscService;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class Backup
*
* @package OCA\Backup\Command
*/
class Create extends Base {
/** @var BackupService */
private $backupService;
/** @var MiscService */
private $miscService;
/**
* Backup constructor.
*
* @param BackupService $backupService
* @param MiscService $miscService
*/
public function __construct(BackupService $backupService, MiscService $miscService) {
$this->backupService = $backupService;
$this->miscService = $miscService;
parent::__construct();
}
/**
*
*/
protected function configure() {
$this->setName('backup:create')
->setDescription('Generate a backup of the instance');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @throws NotPermittedException
* @throws NotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$backup = $this->backupService->backup();
// echo '> ' . json_encode($backup, JSON_PRETTY_PRINT);
}
}

Просмотреть файл

@ -1,87 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2017 Frank Karlitschek <frank@karlitschek.de>
*
* @author Frank Karlitschek <frank@karlitschek.de>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Command;
use OCA\Backup\Backup\Create;
use OCA\Backup\AppInfo\Application;
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 CreateCommand extends Command {
/**
*/
public function __construct() {
parent::__construct();
}
protected function configure() {
$this
->setName('backup:create')
->setDescription('Generate a backup of the instance')
->addArgument(
'path',
InputArgument::REQUIRED,
'The path where the backup should be created'
)
->addOption(
'password',
'pass',
InputOption::VALUE_REQUIRED,
'Optionally password for an encrypted backup',
''
)
;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$path = $input->getArgument('path');
if ($path == '') {
$output->writeln('Backup path not defined');
return 1;
}
$password = $input->getOption('password');
if ($password == '') {
$output->writeln('Password not specified');
return 1;
}
$backup = new \OCA\Backup\Backup\Create($path);
$backup -> password($password);
$backup -> create();
return 0;
}
}

159
lib/Command/Details.php Executable file
Просмотреть файл

@ -0,0 +1,159 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Frank Karlitschek <frank@karlitschek.de>
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Command;
use OC\Core\Command\Base;
use OCA\Backup\Exceptions\ArchiveDeleteException;
use OCA\Backup\Exceptions\BackupNotFoundException;
use OCA\Backup\Exceptions\EncryptionKeyException;
use OCA\Backup\Service\ArchiveService;
use OCA\Backup\Service\BackupService;
use OCA\Backup\Service\CliService;
use OCA\Backup\Service\MiscService;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\ConsoleOutput;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class Backup
*
* @package OCA\Backup\Command
*/
class Details extends Base {
/** @var IAppData */
private $appData;
/** @var CliService */
private $cliService;
/** @var BackupService */
private $backupService;
/** @var ArchiveService */
private $archiveService;
/** @var MiscService */
private $miscService;
/**
* Backup constructor.
*
* @param IAppData $appData
* @param CliService $cliService
* @param BackupService $backupService
* @param ArchiveService $archiveService
* @param MiscService $miscService
*/
public function __construct(
IAppData $appData, CliService $cliService, BackupService $backupService,
ArchiveService $archiveService, MiscService $miscService
) {
$this->appData = $appData;
$this->cliService = $cliService;
$this->backupService = $backupService;
$this->archiveService = $archiveService;
$this->miscService = $miscService;
parent::__construct();
}
/**
*
*/
protected function configure() {
$this->setName('backup:details')
->addArgument('token', InputArgument::REQUIRED, 'token of the backup')
->addOption('source', 's', InputOption::VALUE_REQUIRED, 'specify source')
->addOption(
'check', 'c', InputOption::VALUE_NONE, 'verify the integrity of encrypted files'
)
->addOption(
'key', 'k', InputOption::VALUE_REQUIRED,
'verify the integrity of the decrypted archive files'
)
->addOption('json', 'j', InputOption::VALUE_NONE, 'display json')
->setDescription('Details about a backup');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @throws BackupNotFoundException
* @throws NotFoundException
* @throws NotPermittedException
* @throws EncryptionKeyException
* @throws ArchiveDeleteException
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$output = new ConsoleOutput();
$output = $output->section();
$this->cliService->init($input, $output);
/** TODO: manage sources:
* - default is appdata: we assume the files are in oc_cache and the world is a nice place.
* - source could be in the format files://user@path/ if backups files are available in
* the files app
* - it would also be nice to add a PHP script at the root of the folder that contains
* the backup. The script would:
* > download the right version of nextcloud (from backup.json),
* > auto-extract the archives files at the right place.
*/
$token = $input->getArgument('token');
$backup = $this->backupService->getBackup($token);
if ($input->getOption('json')) {
echo json_encode($backup, JSON_PRETTY_PRINT) . "\n";
return;
}
$this->cliService->displayBackupResume($backup);
$this->cliService->displayBackupDetails($backup);
}
}

99
lib/Command/Listing.php Executable file
Просмотреть файл

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Frank Karlitschek <frank@karlitschek.de>
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Command;
use OC\Core\Command\Base;
use OCA\Backup\Service\BackupService;
use OCA\Backup\Service\MiscService;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
/**
* Class Backup
*
* @package OCA\Backup\Command
*/
class Listing extends Base {
/** @var BackupService */
private $backupService;
/** @var MiscService */
private $miscService;
/**
* Backup constructor.
*
* @param BackupService $backupService
* @param MiscService $miscService
*/
public function __construct(BackupService $backupService, MiscService $miscService) {
$this->backupService = $backupService;
$this->miscService = $miscService;
parent::__construct();
}
/**
*
*/
protected function configure() {
$this->setName('backup:list')
->setDescription('get list of available backups');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @throws NotPermittedException
* @throws NotFoundException
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$backups = $this->backupService->listing();
foreach ($backups as $backup) {
echo ' ' . date("Y-m-d H:i:s", $backup->getCreation()) . ' - ' . $backup->getId()
. "\n";
}
}
}

112
lib/Command/Restore.php Executable file
Просмотреть файл

@ -0,0 +1,112 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Frank Karlitschek <frank@karlitschek.de>
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Command;
use OC\Core\Command\Base;
use OCA\Backup\Exceptions\BackupNotFoundException;
use OCA\Backup\Exceptions\EncryptionKeyException;
use OCA\Backup\Service\BackupService;
use OCA\Backup\Service\MiscService;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
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 Backup
*
* @package OCA\Backup\Command
*/
class Restore extends Base {
/** @var BackupService */
private $backupService;
/** @var MiscService */
private $miscService;
/**
* Backup constructor.
*
* @param BackupService $backupService
* @param MiscService $miscService
*/
public function __construct(BackupService $backupService, MiscService $miscService) {
$this->backupService = $backupService;
$this->miscService = $miscService;
parent::__construct();
}
/**
*
*/
protected function configure() {
$this->setName('backup:restore')
->addArgument('token', InputArgument::REQUIRED, 'token of the backup to restore')
->addOption('key', 'k', InputOption::VALUE_REQUIRED, 'specify encryption key')
->addOption(
'files', 'f', InputOption::VALUE_REQUIRED, 'restore only a specific bunch of files'
)
->setDescription('Restore a backup of the instance');
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @throws NotPermittedException
* @throws NotFoundException
* @throws BackupNotFoundException
* @throws EncryptionKeyException
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$token = $input->getArgument('token');
$key = ($input->getOption('key') === null) ? '' : $input->getOption('key');
$files = ($input->getOption('files') === null) ? '' : $input->getOption('files');
$output->writeln('not available (yet) from the occ.');
// $backup = $this->backupService->restore($token, $pass, $files);
// echo '> ' . json_encode($backup, JSON_PRETTY_PRINT);
}
}

Просмотреть файл

@ -1,87 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2017 Frank Karlitschek <frank@karlitschek.de>
*
* @author Frank Karlitschek <frank@karlitschek.de>
*
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Command;
use OCA\Backup\Backup\Restore;
use OCA\Backup\AppInfo\Application;
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 RestoreCommand extends Command {
/**
*/
public function __construct() {
parent::__construct();
}
protected function configure() {
$this
->setName('backup:restore')
->setDescription('Restore a backup of the instance')
->addArgument(
'path',
InputArgument::REQUIRED,
'The path where the backup should be restored from'
)
->addOption(
'password',
'pass',
InputOption::VALUE_REQUIRED,
'Optionally password for an encrypted backup',
''
)
;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$path = $input->getArgument('path');
if ($path == '') {
$output->writeln('Backup path not defined');
return 1;
}
$password = $input->getOption('password');
if ($password == '') {
$output->writeln('Password not specified');
return 1;
}
$backup = new \OCA\Backup\Backup\Restore($path);
$backup -> password($password);
$backup -> restore();
return 0;
}
}

Просмотреть файл

@ -23,8 +23,6 @@
namespace OCA\Backup\Controller;
use OCA\Backup\AppInfo\Application;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;

81
lib/Db/BackupsRequest.php Normal file
Просмотреть файл

@ -0,0 +1,81 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Db;
use OCA\Backup\Model\Backup;
/**
* Class BackupsRequest
*
* @package OCA\Backup\Db
*/
class BackupsRequest extends BackupsRequestBuilder {
/**
* create a new Person in the database.
*
* @param Backup $backup
*
* @return int
*/
public function create(Backup $backup): int {
$qb = $this->getBackupsInsertSql();
// $qb->setValue('id', $qb->createNamedParameter($id))
//// ->setValue('type', $qb->createNamedParameter($actor->getType()))
// ->setValue('user_id', $qb->createNamedParameter($actor->getUserId()))
// ->setValue('name', $qb->createNamedParameter($actor->getName()))
// ->setValue('summary', $qb->createNamedParameter($actor->getSummary()))
// ->setValue(
// 'creation',
// $qb->createNamedParameter(new DateTime('now'), IQueryBuilder::PARAM_DATE)
// );
return $qb->execute();
}
/**
* @param Backup $backup
*/
public function update(Backup $backup) {
$qb = $this->getBackupsUpdateSql();
$this->limitToId($qb, $backup->getId());
$qb->execute();
}
}

Просмотреть файл

@ -0,0 +1,122 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Db;
use daita\MySmallPhpTools\Traits\TArrayTools;
use OCA\Backup\Model\Backup;
use OCP\DB\QueryBuilder\IQueryBuilder;
/**
* Class BackupRequestBuilder
*
* @package OCA\Backup\Db
*/
class BackupsRequestBuilder extends CoreRequestBuilder {
use TArrayTools;
/**
* Base of the Sql Insert request
*
* @return IQueryBuilder
*/
protected function getBackupsInsertSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->insert(self::SQL_TABLES['BACKUPS']);
return $qb;
}
/**
* Base of the Sql Update request
*
* @return IQueryBuilder
*/
protected function getBackupsUpdateSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->update(self::SQL_TABLES['BACKUPS']);
return $qb;
}
/**
* Base of the Sql Select request for Shares
*
* @return IQueryBuilder
*/
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';
return $qb;
}
/**
* Base of the Sql Delete request
*
* @return IQueryBuilder
*/
protected function getBackupsDeleteSql(): IQueryBuilder {
$qb = $this->dbConnection->getQueryBuilder();
$qb->delete(self::SQL_TABLES['BACKUPS']);
return $qb;
}
/**
* @param array $data
*
* @return Backup
*/
protected function parseBackupsSelectSql($data): Backup {
$backup = new Backup();
$backup->importFromDatabase($data);
return $backup;
}
}

Просмотреть файл

@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Db;
use daita\MySmallPhpTools\Db\RequestBuilder;
use OCA\Backup\Service\MiscService;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
/**
* Class CoreRequestBuilder
*
* @package OCA\Backup\Db
*/
class CoreRequestBuilder extends RequestBuilder {
const SQL_TABLES = [
'BACKUPS' => 'backups'
];
/** @var IDBConnection */
protected $dbConnection;
/** @var MiscService */
protected $miscService;
/** @var string */
protected $defaultSelectAlias;
/**
* CoreRequestBuilder constructor.
*
* @param IDBConnection $connection
* @param MiscService $miscService
*/
public function __construct(IDBConnection $connection, MiscService $miscService) {
$this->dbConnection = $connection;
$this->miscService = $miscService;
}
/**
* Limit the request to the Id
*
* @param IQueryBuilder $qb
* @param int $id
*/
protected function limitToId(IQueryBuilder &$qb, int $id) {
$this->limitToDBFieldInt($qb, 'id', $id);
}
/**
* Limit the request to the status
*
* @param IQueryBuilder $qb
* @param int $status
*/
protected function limitToStatus(IQueryBuilder &$qb, int $status) {
$this->limitToDBFieldInt($qb, 'status', $status);
}
/**
* 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');
}
}

Просмотреть файл

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Exceptions;
class ArchiveCreateException extends \Exception {
}

Просмотреть файл

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Exceptions;
class ArchiveDeleteException extends \Exception {
}

Просмотреть файл

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Exceptions;
class ArchiveNotFoundException extends \Exception {
}

Просмотреть файл

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Exceptions;
class BackupAppCopyException extends \Exception {
}

Просмотреть файл

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Exceptions;
class BackupFolderException extends \Exception {
}

Просмотреть файл

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Exceptions;
class BackupNotFoundException extends \Exception {
}

Просмотреть файл

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Exceptions;
class BackupScriptNotFoundException extends \Exception {
}

Просмотреть файл

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Exceptions;
class ChunkNotFoundException extends \Exception {
}

Просмотреть файл

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Exceptions;
class EncryptionKeyException extends \Exception {
}

Просмотреть файл

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Exceptions;
class VersionException extends \Exception {
}

54
lib/ISqlDump.php Normal file
Просмотреть файл

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup;
interface ISqlDump {
/**
* @param array $data
*
* @return mixed
*/
public function export(array $data): string;
/**
* @param array $data
* @param resource $read
*
* @return bool
*/
public function import(array $data, $read): bool;
}

39
lib/Mockup/OC/Config.php Normal file
Просмотреть файл

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OC;
use OCP\IConfig;
class Config implements IConfig {
}

39
lib/Mockup/OC/Logger.php Normal file
Просмотреть файл

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OC;
use OCP\ILogger;
class Logger implements ILogger {
}

Просмотреть файл

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCP;
interface IConfig {
}

Просмотреть файл

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCP;
interface ILogger {
}

106
lib/Model/ArchiveFile.php Normal file
Просмотреть файл

@ -0,0 +1,106 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Model;
use daita\MySmallPhpTools\Traits\TArrayTools;
use JsonSerializable;
/**
* Class ArchiveFile
*
* @package OCA\Backup\Model
*/
class ArchiveFile implements JsonSerializable {
use TArrayTools;
/** @var string */
private $name = '';
/**
* ArchiveFile constructor.
*
* @param string $name
*/
public function __construct(string $name = '') {
$this->name = $name;
}
/**
* @return string
*/
public function getName(): string {
return $this->name;
}
/**
* @param string $name
*
* @return ArchiveFile
*/
public function setName(string $name): ArchiveFile {
$this->name = $name;
return $this;
}
/**
* @param array $data
*
* @return ArchiveFile
*/
public function import(array $data): ArchiveFile {
$this->setName($this->get('name', $data, ''));
return $this;
}
/**
* @return array
*/
public function jsonSerialize() {
return
[
'name' => $this->getName()
];
}
}

460
lib/Model/Backup.php Normal file
Просмотреть файл

@ -0,0 +1,460 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Model;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TPathTools;
use daita\MySmallPhpTools\Traits\TStringTools;
use JsonSerializable;
use OCA\Backup\Exceptions\VersionException;
use OCP\Files\SimpleFS\ISimpleFolder;
/**
* Class Backup
*
* @package OCA\Backup\Model
*/
class Backup implements JsonSerializable {
const STATUS_OK = 1;
const STATUS_UPLOAD_OK = 2;
const STATUS_UPLOAD_EVERYWHERE = 4;
const STATUS_FAILED = 64;
const FILE_CONFIG = 1;
use TArrayTools;
use TStringTools;
use TPathTools;
/** @var string */
private $id = '';
/** @var string */
private $name = '';
/** @var int */
private $status = 0;
/** @var int[] */
private $version = [];
/** @var string[] */
private $apps = [];
/** @var BackupChunk[] */
private $chunks = [];
/** @var ISimpleFolder */
private $baseFolder = null;
/** @var Error[] */
private $errors = [];
/** @var RemoteStorage[] */
private $storages = [];
/** @var int */
private $creation = 0;
/** @var bool */
private $local = false;
/** @var string */
private $encryptionKey = '';
/** @var BackupOptions */
private $options = null;
/**
* Backup constructor.
*
* @param bool $init
*/
public function __construct(bool $init = false) {
$this->options = new BackupOptions();
if ($init) {
$this->creation = time();
$this->id = $this->uuid();
$this->name = date("Y-m-d-His", $this->creation) . '-' . $this->getId();
}
}
/**
* @return string
*/
public function getId(): string {
return $this->id;
}
/**
* @param string $id
*
* @return Backup
*/
public function setId(string $id): Backup {
$this->id = $id;
return $this;
}
/**
* @return string
*/
public function getName(): string {
return $this->name;
}
/**
* @param string $name
*
* @return Backup
*/
public function setName(string $name): Backup {
$this->name = $name;
return $this;
}
/**
* @return int
*/
public function getStatus(): int {
return $this->status;
}
/**
* @param int $status
*
* @return Backup
*/
public function setStatus(int $status): Backup {
$this->status = $status;
return $this;
}
/**
* @return Error[]
*/
public function getErrors(): array {
return $this->errors;
}
/**
* @param Error[] $errors
*
* @return Backup
*/
public function setErrors(array $errors): Backup {
$this->errors = $errors;
return $this;
}
/**
* @return RemoteStorage[]
*/
public function getStorages(): array {
return $this->storages;
}
/**
* @param RemoteStorage[] $storages
*
* @return Backup
*/
public function setStorages(array $storages): Backup {
$this->storages = $storages;
return $this;
}
/**
* @return int[]
*/
public function getVersion(): array {
return $this->version;
}
/**
* @param int[] $version
*
* @return Backup
*/
public function setVersion(array $version): Backup {
$this->version = $version;
return $this;
}
/**
* @param array $current
*
* @throws VersionException
*/
public function compareVersion(array $current): void {
// TODO compare version !
throw new VersionException(
'Your nextcloud must be upgraded to at least v' . implode('.', $this->getVersion())
);
}
/**
* @return string[]
*/
public function getApps(): array {
return $this->apps;
}
/**
* @param string[] $apps
*
* @return Backup
*/
public function setApps(array $apps): Backup {
$this->apps = $apps;
return $this;
}
/**
* @param string $appId
*
* @return Backup
*/
public function addApp(string $appId): Backup {
$this->apps[] = $appId;
return $this;
}
/**
* @param bool $filtered
*
* @return BackupChunk[]
*/
public function getChunks($filtered = false): array {
$options = $this->getOptions();
if (!$filtered || $options->getChunk() === '') {
return $this->chunks;
}
$options = $this->getOptions();
foreach ($this->chunks as $chunk) {
if ($chunk->getName() === $options->getChunk()) {
return [$chunk];
}
}
return [];
}
/**
* @param array $chunks
*
* @return Backup
*/
public function setChunks(array $chunks): Backup {
$this->chunks = $chunks;
return $this;
}
/**
* @param BackupChunk $chunk
*
* @return Backup
*/
public function addChunk(BackupChunk $chunk): Backup {
$this->chunks[] = $chunk;
return $this;
}
/**
* @return bool
*/
public function hasBaseFolder(): bool {
return ($this->baseFolder !== null);
}
/**
* @param ISimpleFolder $folder
*
* @return Backup
*/
public function setBaseFolder(ISimpleFolder $folder): Backup {
$this->baseFolder = $folder;
return $this;
}
/**
* @return ISimpleFolder
*/
public function getBaseFolder(): ISimpleFolder {
return $this->baseFolder;
}
/**
* @return int
*/
public function getCreation(): int {
return $this->creation;
}
/**
* @param int $creation
*
* @return Backup
*/
public function setCreation(int $creation): Backup {
$this->creation = $creation;
return $this;
}
/**
* @return bool
*/
public function isLocal(): bool {
return $this->local;
}
/**
* @param bool $local
*
* @return Backup
*/
public function setLocal(bool $local): Backup {
$this->local = $local;
return $this;
}
/**
* @return BackupOptions
*/
public function getOptions(): BackupOptions {
return $this->options;
}
/**
* @param BackupOptions $options
*
* @return Backup
*/
public function setOptions(BackupOptions $options): Backup {
$this->options = $options;
return $this;
}
/**
* @return string
*/
public function getEncryptionKey(): string {
return $this->encryptionKey;
}
/**
* @param string $encryptionKey
*
* @return Backup
*/
public function setEncryptionKey(string $encryptionKey): Backup {
$this->encryptionKey = $encryptionKey;
return $this;
}
/**
* @param array $data
*
* @return Backup
*/
public function import(array $data): Backup {
$this->setId($this->get('id', $data, ''));
$this->setStatus($this->getInt('status', $data));
$this->setName($this->get('name', $data, ''));
$this->setVersion($this->getArray('version', $data, []));
$this->setApps($this->getArray('apps', $data, []));
$this->setCreation($this->getInt('creation', $data, 0));
$this->setChunks($this->getList('chunks', $data, [BackupChunk::class, 'import'], []));
$this->setErrors($this->getList('errors', $data, [Error::class, 'import'], []));
$this->setStorages($this->getList('remotes', $data, [RemoteStorage::class, 'import'], []));
return $this;
}
/**
* @return array
*/
public function jsonSerialize() {
return [
'id' => $this->getId(),
'status' => $this->getStatus(),
'name' => $this->getName(),
'version' => $this->getVersion(),
'apps' => $this->getApps(),
'creation' => $this->getCreation(),
'chunks' => $this->getChunks(),
'errors' => $this->getErrors(),
'storages' => $this->getStorages()
];
}
}

261
lib/Model/BackupArchive.php Normal file
Просмотреть файл

@ -0,0 +1,261 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Model;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TStringTools;
use JsonSerializable;
/**
* Class BackupArchive
*
* @package OCA\Backup\Model
*/
class BackupArchive implements JsonSerializable {
use TArrayTools;
use TStringTools;
/** @var string */
private $name = '';
/** @var string[] */
private $files = [];
/** @var int */
private $count = 0;
/** @var int */
private $size = 0;
/** @var string */
private $checksum = '';
/** @var string */
private $encryptedChecksum = '';
/**
* BackupArchive constructor.
*/
public function __construct() {
$this->name = $this->uuid();
}
/**
* @param string $ext
*
* @return string
*/
public function getName(string $ext = ''): string {
if ($ext === '') {
return $this->name;
}
return $this->name . '.' . $ext;
}
/**
* @param string $name
*
* @return BackupArchive
*/
public function setName(string $name): BackupArchive {
$this->name = $name;
return $this;
}
// /**
// * @return int
// */
// public function count(): int {
// return sizeof($this->files);
// }
//
/**
* @return int
*/
public function getCount(): int {
return $this->count;
}
/**
* @param int $count
*
* @return BackupArchive
*/
public function setCount(int $count = -1): BackupArchive {
if ($count === -1) {
$this->count = sizeof($this->files);
} else {
$this->count = $count;
}
return $this;
}
/**
* @return ArchiveFile[]
*/
public function getFiles(): array {
return $this->files;
}
/**
* @param ArchiveFile[] $files
*
* @return BackupArchive
*/
public function setFiles(array $files): BackupArchive {
$this->files = $files;
return $this;
}
/**
* @param ArchiveFile $file
*
* @return BackupArchive
*/
public function addFile(ArchiveFile $file): BackupArchive {
$this->files[] = $file;
return $this;
}
/**
* @return int
*/
public function getSize(): int {
return $this->size;
}
/**
* @param int $size
*
* @return BackupArchive
*/
public function setSize(int $size): BackupArchive {
$this->size = $size;
return $this;
}
/**
* @return string
*/
public function getChecksum(): string {
return $this->checksum;
}
/**
* @param string $checksum
*
* @return BackupArchive
*/
public function setChecksum(string $checksum): BackupArchive {
$this->checksum = $checksum;
return $this;
}
/**
* @return string
*/
public function getEncryptedChecksum(): string {
return $this->encryptedChecksum;
}
/**
* @param string $encryptedChecksum
*
* @return BackupArchive
*/
public function setEncryptedChecksum(string $encryptedChecksum): BackupArchive {
$this->encryptedChecksum = $encryptedChecksum;
return $this;
}
/**
* @param array $data
*
* @return BackupArchive
*/
public function import(array $data): BackupArchive {
$this->setName($this->get('name', $data, ''))
// ->setFiles($this->getArray('files', $data, []))
->setCount($this->getInt('count', $data, 0))
->setSize($this->getInt('size', $data, 0))
->setChecksum($this->get('checksum', $data, ''))
->setEncryptedChecksum($this->get('encrypted', $data, ''));
return $this;
}
/**
* @return array
*/
public function getResume(): array {
return [
'files' => $this->getFileS()
];
}
/**
* @return array
*/
public function jsonSerialize(): array {
return
[
'name' => $this->getName(),
'count' => $this->getCount(),
'size' => $this->getSize(),
'checksum' => $this->getChecksum(),
'encrypted' => $this->getEncryptedChecksum()
];
}
}

294
lib/Model/BackupChunk.php Normal file
Просмотреть файл

@ -0,0 +1,294 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Model;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TStringTools;
use JsonSerializable;
/**
* Class BackupChunk
*
* @package OCA\Backup\Model
*/
class BackupChunk implements JsonSerializable {
use TArrayTools;
use TStringTools;
const ROOT_DISK = 1;
const ROOT_NEXTCLOUD = 2;
const ROOT_DATA = 3;
const ROOT_APPS = 4;
const FILE_CONFIG = 101;
// value > 1000 is for content that are not 'file'
const SQL_DUMP = 1001;
/** @var string */
private $name = '';
/** @var int */
private $type = 0;
/** @var string */
private $path = '';
/** @var string */
private $root = '';
/** @var string */
private $uniqueFile = '';
/** @var BackupArchive[] */
private $archives = [];
/** @var string[] */
private $files = [];
/**
* BackupChunk constructor.
*
* @param string $name
* @param int $type
* @param string $path
*/
public function __construct(int $type = 0, string $path = '', string $name = '') {
$this->type = $type;
$this->path = $path;
$this->name = $name;
if ($name === '') {
$this->name = $this->uuid();
}
}
/**
* @return string
*/
public function getName(): string {
return $this->name;
}
/**
* @param string $name
*
* @return BackupChunk
*/
public function setName(string $name): BackupChunk {
$this->name = $name;
return $this;
}
/**
* @return int
*/
public function getType(): int {
return $this->type;
}
/**
* @param int $type
*
* @return BackupChunk
*/
public function setType(int $type): BackupChunk {
$this->type = $type;
return $this;
}
/**
* @return string
*/
public function getPath(): string {
return $this->path;
}
/**
* @param string $path
*
* @return BackupChunk
*/
public function setPath(string $path): BackupChunk {
$this->path = $path;
return $this;
}
/**
* @return string
*/
public function getRoot(): string {
return $this->root;
}
/**
* @param string $root
*
* @return BackupChunk
*/
public function setRoot(string $root): BackupChunk {
$this->root = $root;
return $this;
}
/**
* @return string
*/
public function getAbsolutePath(): string {
return $this->getRoot() . $this->getPath();
}
/**
* @param string $path
*
* @return BackupChunk
*/
public function addFile(string $path): BackupChunk {
$this->files[] = $path;
return $this;
}
/**
* @return array
*/
public function getFiles(): array {
return $this->files;
}
/**
* @param string[] $files
*
* @return BackupChunk
*/
public function setFiles(array $files): BackupChunk {
$this->files = $files;
return $this;
}
/**
* @return string
*/
public function getUniqueFile(): string {
return $this->uniqueFile;
}
/**
* @param string $file
*
* @return BackupChunk
*/
public function setUniqueFile(string $file): BackupChunk {
$this->uniqueFile = $file;
return $this;
}
/**
* @return BackupArchive[]
*/
public function getArchives(): array {
return $this->archives;
}
/**
* @param BackupArchive[] $archives
*
* @return BackupChunk
*/
public function setArchives(array $archives): BackupChunk {
$this->archives = $archives;
return $this;
}
/**
* @param BackupArchive $archive
*
* @return BackupChunk
*/
public function addArchive(BackupArchive $archive): BackupChunk {
$this->archives[] = $archive;
return $this;
}
/**
* @param array $data
*
* @return BackupChunk
*/
public function import(array $data): BackupChunk {
$this->setType($this->getInt('type', $data, 0));
$this->setName($this->get('name', $data, ''));
$this->setRoot($this->get('root', $data, ''));
$this->setPath($this->get('path', $data, ''));
$this->setArchives($this->getList('archives', $data, [BackupArchive::class, 'import'], []));
return $this;
}
/**
* @return array
*/
public function jsonSerialize() {
return [
'name' => $this->getName(),
'type' => $this->getType(),
'root' => $this->getRoot(),
'path' => $this->getPath(),
'archives' => $this->getArchives()
];
}
}

281
lib/Model/BackupOptions.php Normal file
Просмотреть файл

@ -0,0 +1,281 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Model;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TPathTools;
use JsonSerializable;
/**
* Class BackupOptions
*
* @package OCA\Backup\Model
*/
class BackupOptions implements JsonSerializable {
use TArrayTools;
use TPathTools;
/** @var string */
private $newRoot = '';
/** @var string */
private $path = '';
/** @var string */
private $search = '';
/** @var array */
private $config = [];
/** @var string */
private $chunk = '';
/** @var string */
private $archive = '';
/** @var bool */
private $all = false;
/** @var bool */
private $fixDataDir = false;
/**
* BackupOptions constructor.
*/
public function __construct() {
}
/**
* @return string
*/
public function getNewRoot(): string {
return $this->newRoot;
}
/**
* @param string $root
*
* @return BackupOptions
*/
public function setNewRoot(string $root): BackupOptions {
if ($root !== '') {
$root = $this->withEndSlash($root);
}
$this->newRoot = $root;
return $this;
}
/**
* @return string
*/
public function getPath(): string {
return $this->path;
}
/**
* @param string $path
*
* @return BackupOptions
*/
public function setPath(string $path): BackupOptions {
$this->path = $path;
return $this;
}
/**
* @return string
*/
public function getSearch(): string {
return $this->search;
}
/**
* @param string $search
*
* @return BackupOptions
*/
public function setSearch(string $search): BackupOptions {
$this->search = $search;
return $this;
}
/**
* @return array
*/
public function getConfig(): array {
return $this->config;
}
/**
* @param array $config
*
* @return BackupOptions
*/
public function setConfig(array $config): BackupOptions {
$this->config = $config;
return $this;
}
/**
* @param string $json
*
* @return BackupOptions
*/
public function setConfigRaw(string $json): BackupOptions {
$config = json_decode($json, true);
if (is_array($config)) {
$this->setConfig($config);
}
return $this;
}
/**
* @return string
*/
public function getChunk(): string {
return $this->chunk;
}
/**
* @param string $chunk
*
* @return BackupOptions
*/
public function setChunk(string $chunk): BackupOptions {
$this->chunk = $chunk;
return $this;
}
/**
* @return string
*/
public function getArchive(): string {
return $this->archive;
}
/**
* @param string $archive
*
* @return BackupOptions
*/
public function setArchive(string $archive): BackupOptions {
$this->archive = $archive;
return $this;
}
/**
* @return bool
*/
public function isAll(): bool {
return $this->all;
}
/**
* @param bool $all
*
* @return BackupOptions
*/
public function setAll(bool $all): BackupOptions {
$this->all = $all;
return $this;
}
/**
* @return bool
*/
public function isFixDataDir(): bool {
return $this->fixDataDir;
}
/**
* @param bool $fixDataDir
*/
public function setFixDataDir(bool $fixDataDir): void {
$this->fixDataDir = $fixDataDir;
}
/**
* @param array $data
*
* @return BackupOptions
*/
public function import(array $data): BackupOptions {
$this->setNewRoot($this->get('newRoot', $data, ''));
$this->setPath($this->get('path', $data, ''));
$this->setSearch($this->get('search', $data, ''));
$this->setArchive($this->get('archive', $data, ''));
$this->setChunk($this->get('chunk', $data, ''));
$this->setAll($this->getBool('all', $data, false));
return $this;
}
/**
* @return array
*/
public function jsonSerialize() {
return [
'newRoot' => $this->getNewRoot(),
'search' => $this->getSearch(),
'path' => $this->getPath(),
'archive' => $this->getArchive(),
'chunk' => $this->getChunk(),
'all' => $this->isAll()
];
}
}

67
lib/Model/Error.php Normal file
Просмотреть файл

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Model;
use JsonSerializable;
/**
* Class BackupError
*
* @package OCA\Backup\Model
*/
class Error implements JsonSerializable {
public function __construct() {
}
/**
* @param array $data
*
* @return Error
*/
public function import(array $data): Error {
return $this;
}
/**
* @return array
*/
public function jsonSerialize() {
return [];
}
}

Просмотреть файл

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Model;
use JsonSerializable;
/**
* Class BackupError
*
* @package OCA\Backup\Model
*/
class RemoteStorage implements JsonSerializable {
public function __construct() {
}
/**
* @param array $data
*
* @return RemoteStorage
*/
public function import(array $data): RemoteStorage {
return $this;
}
/**
* @return array
*/
public function jsonSerialize() {
return [];
}
}

Просмотреть файл

@ -0,0 +1,590 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Service;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TFileTools;
use daita\MySmallPhpTools\Traits\TStringTools;
use Exception;
use OCA\Backup\Exceptions\ArchiveCreateException;
use OCA\Backup\Exceptions\ArchiveDeleteException;
use OCA\Backup\Exceptions\ArchiveNotFoundException;
use OCA\Backup\Exceptions\BackupAppCopyException;
use OCA\Backup\Exceptions\BackupFolderException;
use OCA\Backup\Exceptions\BackupScriptNotFoundException;
use OCA\Backup\Exceptions\EncryptionKeyException;
use OCA\Backup\Model\ArchiveFile;
use OCA\Backup\Model\Backup;
use OCA\Backup\Model\BackupArchive;
use OCA\Backup\Model\BackupChunk;
use ZipArchive;
use ZipStreamer\COMPR;
use ZipStreamer\ZipStreamer;
/**
* Class ArchiveService
*
* @package OCA\Backup\Service
*/
class ArchiveService {
use TArrayTools;
use TStringTools;
use TFileTools;
const MAX_ZIP_SIZE = 100000000;
/** @var FilesService */
private $filesService;
/** @var EncryptService */
private $encryptService;
/** @var MiscService */
private $miscService;
/**
* ArchiveService constructor.
*
* @param FilesService $filesService
* @param EncryptService $encryptService
* @param MiscService $miscService
*/
public function __construct(
FilesService $filesService, EncryptService $encryptService, MiscService $miscService
) {
$this->filesService = $filesService;
$this->encryptService = $encryptService;
$this->miscService = $miscService;
}
/**
* @param Backup $backup
*
* @return bool
* @throws ArchiveCreateException
* @throws ArchiveDeleteException
* @throws ArchiveNotFoundException
*/
public function getArchives(Backup $backup): bool {
foreach ($backup->getChunks(false) as $chunk) {
$this->filesService->initBackupChunk($chunk);
$this->filesService->fillBackupChunk($chunk, $chunk->getUniqueFile());
$this->generateArchives($backup, $chunk);
}
return true;
}
/**
* @param Backup $backup
* @param BackupArchive $archive
* @param string $root
*
* @throws ArchiveDeleteException
* @throws ArchiveNotFoundException
* @throws EncryptionKeyException
* @throws BackupFolderException
*/
public function extractAll(Backup $backup, BAckupArchive $archive, string $root): void {
if (!is_dir($root)) {
if (!@mkdir($root, 0755, true)) {
throw new BackupFolderException('could not create ' . $root);
}
}
$this->decryptArchive($backup, $archive);
$this->extractAllFromArchive($archive, $root);
$this->deleteArchive($backup, $archive, 'zip');
}
/**
* @param BackupArchive $archive
* @param string $root
*
* @throws ArchiveNotFoundException
* @throws Exception
*/
public function extractAllFromArchive(BackupArchive $archive, string $root): void {
$zip = $this->openZipArchive($archive);
$zip->extractTo($root);
$this->closeZipArchive($zip);
unlink($root . '.backup.' . $archive->getName() . '.json');
}
/**
* @param BackupArchive $archive
*
* @return ZipArchive
* @throws ArchiveNotFoundException
*/
public function openZipArchive(BackupArchive $archive): ZipArchive {
$zip = new ZipArchive();
if (($err = $zip->open($archive->getName('zip'))) !== true) {
throw new ArchiveNotFoundException('Could not open Zip Archive (' . $err . ')');
}
return $zip;
}
/**
* @param ZipArchive $zip
*/
public function closeZipArchive(ZipArchive $zip): void {
$zip->close();
}
/**
* @param BackupArchive $archive
* @param ZipArchive $zip
*/
public function listFilesFromZip(BackupArchive $archive, ZipArchive $zip): void {
$json = $zip->getFromName('.backup.' . $archive->getName() . '.json');
$data = json_decode($json, true);
$files = $this->getList('files', $data, [ArchiveFile::class, 'import'], []);
$archive->setFiles($files);
}
/**
* @param ZipArchive $zip
* @param string $root
* @param ArchiveFile[] $archiveFiles
*/
public function extractFilesFromZip(ZipArchive $zip, string $root, array $archiveFiles): void {
$files = array_map(
function(ArchiveFile $entry) {
return $entry->getName();
}, $archiveFiles
);
$zip->extractTo($root, $files);
}
/**
* @param ZipArchive $zip
* @param string $file
*
* @return resource
*/
public function extractContentFromZip(ZipArchive $zip, string $file) {
return $zip->getStream($file);
}
/**
* @param Backup $backup
* @param BackupArchive $archive
* @param bool $encrypted
*
* @return bool
* @throws ArchiveNotFoundException
*/
public function verifyChecksum(Backup $backup, BackupArchive $archive, bool $encrypted): bool {
$sum = $this->getChecksum($backup, $archive, $encrypted);
if (!$encrypted && $sum === $archive->getChecksum()) {
return true;
}
if ($encrypted && $sum === $archive->getEncryptedChecksum()) {
return true;
}
return false;
}
/**
* @param Backup $backup
* @param BackupChunk $backupChunk
*
* @throws ArchiveCreateException
* @throws ArchiveNotFoundException
* @throws ArchiveDeleteException
*/
private function generateArchives(Backup $backup, BackupChunk $backupChunk) {
$files = $backupChunk->getFiles();
while (!empty($files)) {
$archive = $this->createArchiveWithFiles($backup, $backupChunk, $files);
$this->updateChecksum($backup, $archive, false);
$this->encryptArchive($backup, $archive, true);
$this->updateChecksum($backup, $archive, true);
$backupChunk->addArchive($archive);
}
}
/**
* @param Backup $backup
* @param BackupChunk $chunk
* @param string $filename
* @param string $content
*
* @return void
* @throws ArchiveCreateException
* @throws ArchiveDeleteException
* @throws ArchiveNotFoundException
*/
public function createContentArchive(
Backup $backup, BackupChunk $chunk, string $filename, string $content
): void {
$archive = new BackupArchive();
$archive->setCount(1);
$archive->addFile(new ArchiveFile($filename));
$archive->setSize(strlen($content));
$chunk->addArchive($archive);
$zip = $this->generateZip($backup, $archive->getName('zip'));
$read = fopen('data://text/plain,' . $content, 'r');
$zip->addFileFromStream($read, $filename);
$zip->finalize();
$this->updateChecksum($backup, $archive, false);
$this->encryptArchive($backup, $archive, true);
$this->updateChecksum($backup, $archive, true);
}
/**
* @param Backup $backup
* @param BackupChunk $backupChunk
* @param array $files
*
* @return BackupArchive
* @throws ArchiveCreateException
*/
private function createArchiveWithFiles(Backup $backup, BackupChunk $backupChunk, array &$files
): BackupArchive {
$archive = new BackupArchive();
$zip = $this->generateZip($backup, $archive->getName('zip'));
$zipSize = 0;
while (($filename = array_shift($files)) !== null) {
$fileSize = filesize($backupChunk->getAbsolutePath() . $filename);
if ($zipSize > 0 && ($zipSize + $fileSize) > self::MAX_ZIP_SIZE) {
$this->finalizeZip($zip, $archive->setSize($zipSize));
return $archive;
}
$zipSize += $fileSize;
$in = fopen($backupChunk->getAbsolutePath() . $filename, 'r');
$zip->addFileFromStream($in, $filename);
$archiveFile = new ArchiveFile($filename);
$archive->addFile($archiveFile);
$archive->setCount();
}
$this->finalizeZip($zip, $archive->setSize($zipSize));
return $archive;
}
/**
* @param Backup $backup
* @param string $filename
*
* @return ZipStreamer
* @throws ArchiveCreateException
*/
public function generateZip(Backup $backup, string $filename) {
if (!$backup->hasBaseFolder()) {
throw new ArchiveCreateException('Backup has no Base Folder');
}
$folder = $backup->getBaseFolder();
try {
$file = $folder->newFile($filename);
$zip = new ZipStreamer(
[
'outstream' => $file->write(),
'zip64' => false,
'compress' => COMPR::DEFLATE,
'level' => $this->assignCompressionLevel()
]
);
} catch (Exception $e) {
throw new ArchiveCreateException(
'could not create Zip archive (' . $e->getMessage() . ')'
);
}
return $zip;
}
/**
* @param ZipStreamer $zip
* @param BackupArchive $archive
*/
public function finalizeZip(ZipStreamer $zip, BackupArchive $archive): void {
$str = json_encode($archive->getResume(), JSON_PRETTY_PRINT);
$read = fopen('data://text/plain,' . $str, 'r');
$zip->addFileFromStream($read, '.backup.' . $archive->getName() . '.json');
$zip->finalize();
}
/**
* @param Backup $backup
* @param BackupArchive $archive
* @param bool $encrypted
*
* @throws ArchiveNotFoundException
*/
private function updateChecksum(Backup $backup, BackupArchive $archive, bool $encrypted): void {
$sum = $this->getChecksum($backup, $archive, $encrypted);
if ($encrypted) {
$archive->setEncryptedChecksum($sum);
} else {
$archive->setChecksum($sum);
}
}
/**
* @param Backup $backup
* @param BackupArchive $archive
* @param bool $encrypted
*
* @return string
* @throws ArchiveNotFoundException
*/
private function getChecksum(Backup $backup, BackupArchive $archive, bool $encrypted): string {
try {
if ($backup->isLocal()) {
if (!file_exists('./' . $archive->getName(($encrypted) ? '' : 'zip'))) {
throw new ArchiveNotFoundException('Archive not found');
}
$stream = fopen('./' . $archive->getName(($encrypted) ? '' : 'zip'), 'r');
} else {
$folder = $backup->getBaseFolder();
$file = $folder->getFile($archive->getName(($encrypted) ? '' : 'zip'));
$stream = $file->read();
}
} catch (Exception $e) {
throw new ArchiveNotFoundException('Archive not found');
}
if (is_bool($stream)) {
throw new ArchiveNotFoundException('Archive not found');
}
return $this->getChecksumFromStream($stream);
}
/**
* @param Backup $backup
* @param BackupArchive $archive
* @param string $ext
*
* @throws ArchiveDeleteException
*/
public function deleteArchive(Backup $backup, BackupArchive $archive, $ext = '') {
if ($backup->isLocal()) {
unlink('./' . $archive->getName($ext));
} else {
$folder = $backup->getBaseFolder();
try {
$file = $folder->getFile($archive->getName($ext));
$file->delete();
} catch (Exception $e) {
throw new ArchiveDeleteException('Could not delete Archive');
}
}
}
/**
* @param Backup $backup
* @param BackupArchive $archive
* @param bool $delete
*
* @throws ArchiveNotFoundException
* @throws ArchiveDeleteException
*/
public function encryptArchive(Backup $backup, BackupArchive $archive, bool $delete): void {
$folder = $backup->getBaseFolder();
try {
$file = $folder->getFile($archive->getName('zip'));
} catch (Exception $e) {
throw new ArchiveNotFoundException('Could not read Archive to encrypt');
}
try {
$encrypted = $folder->newFile($archive->getName());
} catch (Exception $e) {
throw new ArchiveNotFoundException('Could not write to encrypted Archive');
}
$key = substr(sha1($backup->getEncryptionKey(), true), 0, 16);
try {
$this->encryptService->encryptFile($file->read(), $encrypted->write(), $key);
} catch (Exception $e) {
throw new ArchiveNotFoundException('Could not encrypt Archive');
}
if ($delete) {
try {
$file->delete();
} catch (Exception $e) {
throw new ArchiveDeleteException('Could not delete non-encrypted Archive !');
}
}
}
/**
* @param Backup $backup
* @param BackupArchive $archive
*
* @throws EncryptionKeyException
* @throws ArchiveNotFoundException
* @throws ArchiveNotFoundException
*/
public function decryptArchive(Backup $backup, BackupArchive $archive) {
if ($backup->isLocal()) {
if (!file_exists('./' . $archive->getName())) {
throw new ArchiveNotFoundException('Archive not found');
}
$stream = fopen('./' . $archive->getName(''), 'r');
$write = fopen('./' . $archive->getName('zip'), 'w');
} else {
$folder = $backup->getBaseFolder();
try {
$encrypted = $folder->getFile($archive->getName());
$stream = $encrypted->read();
} catch (Exception $e) {
throw new ArchiveNotFoundException('Archive not found');
}
try {
$file = $folder->newFile($archive->getName('zip'));
$write = $file->write();
} catch (Exception $e) {
throw new ArchiveNotFoundException('Zip file not created');
}
}
$key = substr(sha1($backup->getEncryptionKey(), true), 0, 16);
$this->encryptService->decryptFile($stream, $write, $key);
}
/**
* @param Backup $backup
*
* @throws BackupScriptNotFoundException
* @throws BackupAppCopyException
*/
public function copyApp(Backup $backup): void {
$folder = $backup->getBaseFolder();
try {
$file = $folder->newFile('app.zip');
$zip = new ZipStreamer(
[
'outstream' => $file->write(),
'zip64' => false,
'compress' => COMPR::DEFLATE,
'level' => $this->assignCompressionLevel()
]
);
} catch (Exception $e) {
throw new BackupAppCopyException('Could not generate app.zip');
}
$appFiles = $this->filesService->getFilesFromApp();
foreach ($appFiles as $file) {
if ($file === 'backup.php') {
continue;
}
$in = fopen(FilesService::APP_ROOT . $file, 'rb');
$zip->addFileFromStream($in, './app/' . $file);
}
$zip->finalize();
$script = file_get_contents(FilesService::APP_ROOT . 'backup.php');
try {
$scriptFile = $folder->newFile('backup.php');
$scriptFile->putContent($script);
} catch (Exception $e) {
throw new BackupScriptNotFoundException('Could not create backup.php');
}
}
/**
* @return int
*/
private function assignCompressionLevel(): int {
try {
$extension = new \ReflectionExtension('http');
} catch (\ReflectionException $e) {
return COMPR::NONE;
}
if (version_compare($extension->getVersion(), '0.10.0', '>=')) {
return COMPR::MAXIMUM;
}
return COMPR::NONE;
}
}

Просмотреть файл

@ -0,0 +1,479 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Service;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TFileTools;
use daita\MySmallPhpTools\Traits\TStringTools;
use Exception;
use OC;
use OCA\Backup\AppInfo\Application;
use OCA\Backup\Exceptions\ArchiveCreateException as ArchiveCreateExceptionAlias;
use OCA\Backup\Exceptions\ArchiveDeleteException;
use OCA\Backup\Exceptions\ArchiveNotFoundException;
use OCA\Backup\Exceptions\BackupAppCopyException;
use OCA\Backup\Exceptions\BackupFolderException;
use OCA\Backup\Exceptions\BackupNotFoundException;
use OCA\Backup\Exceptions\BackupScriptNotFoundException as BackupScriptNotFoundExceptionAlias;
use OCA\Backup\Exceptions\ChunkNotFoundException;
use OCA\Backup\Model\Backup;
use OCA\Backup\Model\BackupChunk;
use OCA\Backup\SqlDump\SqlDumpMySQL;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Util;
/**
* Class BackupService
*
* @package OCA\Backup\Service
*/
class BackupService {
use TArrayTools;
use TStringTools;
use TFileTools;
const NOBACKUP_FILE = '.nobackup';
const SUMMARY_FILE = 'backup.json';
const SQL_DUMP_FILE = 'backup_sql';
/** @var IAppData */
private $appData;
/** @var ArchiveService */
private $archiveService;
/** @var FilesService */
private $filesService;
/** @var ConfigService */
private $configService;
/** @var MiscService */
private $miscService;
/**
* BackupService constructor.
*
* @param ArchiveService $archiveService
* @param FilesService $filesService
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(
ArchiveService $archiveService, FilesService $filesService, ConfigService $configService,
MiscService $miscService
) {
if (class_exists(OC::class)) {
$this->appData = OC::$server->getAppDataDir(Application::APP_ID);
}
$this->archiveService = $archiveService;
$this->filesService = $filesService;
$this->configService = $configService;
$this->miscService = $miscService;
}
/**
* @return Backup
* @throws ArchiveNotFoundException
* @throws NotPermittedException
* @throws ArchiveCreateExceptionAlias
* @throws ArchiveDeleteException
* @throws BackupAppCopyException
* @throws BackupScriptNotFoundExceptionAlias
* @throws NotFoundException
*/
public function backup(): Backup {
$backup = $this->initBackup();
$this->archiveService->copyApp($backup);
$backup->setEncryptionKey('12345');
$this->archiveService->getArchives($backup);
$this->backupSql($backup);
$this->endBackup($backup);
return $backup;
}
/**
* @return Backup[]
* @throws NotPermittedException
* @throws NotFoundException
*/
public function listing(): array {
$this->initBackupFS();
$backups = [];
$ls = $this->appData->getDirectoryListing();
foreach ($ls as $entry) {
if ($ls === 'remote') {
continue;
}
try {
$backups[] = $this->parseFromFolder($entry->getName());
} catch (BackupFolderException $e) {
} catch (NotPermittedException $e) {
}
}
return $backups;
}
// /**
// * @param string $token
// * @param string $pass
// * @param string $file
// *
// * @return Backup
// * @throws BackupNotFoundException
// * @throws NotFoundException
// * @throws NotPermittedException
// */
// public function restore(string $token, string $pass, string $file = ''): Backup {
// $backup = $this->getBackup($token);
//
// foreach ($backup->getFiles() as $files) {
// if ($file === '' || $files->getName() === $file) {
// $this->restoreBackupChunk($backup, $files, $pass);
// }
// }
//
// return $backup;
// }
/**
* @param Backup $backup
* @param BackupChunk $files
*
* @return string
*/
public function getExtractRoot(Backup $backup, BackupChunk $files): string {
$absoluteRootLength = strlen($this->getAbsoluteRoot($backup));
$base = substr($files->getRoot(), $absoluteRootLength);
$options = $backup->getOptions();
$root = ($options->getNewRoot() === '') ? $files->getRoot() : $options->getNewRoot();
$root .= $base . $files->getPath();
return $root;
}
/**
* @param Backup $backup
*/
public function restoreBackup(Backup $backup): void {
foreach ($backup->getChunks(true) as $backupChunk) {
$this->restoreBackupChunk($backup, $backupChunk);
}
}
/**
* @param Backup $backup
* @param BackupChunk $files
*/
public function restoreBackupChunk(Backup $backup, BackupChunk $files): void {
if ($files->getType() >= BackupChunk::SQL_DUMP) {
$this->restoreNonFileChunk($backup, $files);
return;
}
$root = $this->getExtractRoot($backup, $files);
foreach ($files->getArchives() as $archive) {
try {
$this->archiveService->extractAll($backup, $archive, $root);
} catch (Exception $e) {
//echo $e->getMessage() . "\n";
}
}
}
/**
* @param Backup $backup
* @param BackupChunk $files
*/
private function restoreNonFileChunk(Backup $backup, BackupChunk $files): void {
switch ($files->getType()) {
case BackupChunk::SQL_DUMP:
$this->restoreSqlDump($backup, $files);
break;
}
}
/**
* @param Backup $backup
* @param BackupChunk $files
*/
private function restoreSqlDump(Backup $backup, BackupChunk $files): void {
// $sqlDump = new SqlDumpMySQL();
// $content = $sqlDump->import();
}
/**
* @param string $token
*
* @return Backup
* @throws NotFoundException
* @throws NotPermittedException
* @throws BackupNotFoundException
*/
public function getBackup(string $token): Backup {
$backups = $this->listing();
foreach ($backups as $backup) {
if ($backup->getId() === $token) {
return $backup;
}
}
throw new BackupNotFoundException('Backup not found');
}
/**
* @param Backup $backup
*
* @return string
*/
public function getAbsoluteRoot(Backup $backup) {
$compared = false;
$absoluteRoot = '';
foreach ($backup->getChunks() as $backupChunk) {
if ($backupChunk->getType() >= BackupChunk::SQL_DUMP) {
continue;
}
if (!$compared) {
$absoluteRoot = $backupChunk->getRoot();
$compared = true;
continue;
}
$absoluteRoot = $this->commonPart($absoluteRoot, $backupChunk->getRoot());
}
return $absoluteRoot;
}
/**
* @return Backup
* @throws NotPermittedException
*/
private function initBackup(): Backup {
$this->initBackupFS();
$backup = new Backup(true);
$backup->setVersion(Util::getVersion());
$folder = $this->appData->newFolder('/' . $backup->getName());
$temp = $folder->newFile(self::SUMMARY_FILE);
$temp->putContent('');
$backup->setBaseFolder($folder);
$backup->addChunk(new BackupChunk(BackupChunk::ROOT_DATA, '', 'data'));
$backup->addChunk(new BackupChunk(BackupChunk::ROOT_NEXTCLOUD, 'apps/', 'apps'));
$backup->addChunk(new BackupChunk(BackupChunk::FILE_CONFIG, '', 'config'));
$this->addCustomAppsChunk($backup);
return $backup;
}
/**
* @throws NotPermittedException
*/
private function initBackupFS() {
$path = '/';
try {
$folder = $this->appData->getFolder($path);
} catch (NotFoundException $e) {
$folder = $this->appData->newFolder($path);
}
$folder->newFile(self::NOBACKUP_FILE);
}
/**
* @param Backup $backup
*
* @throws NotPermittedException
* @throws NotFoundException
*/
private function endBackup(Backup $backup) {
if (!$backup->hasBaseFolder()) {
return;
}
$folder = $backup->getBaseFolder();
$json = $folder->getFile(self::SUMMARY_FILE);
$json->putContent(json_encode($backup, JSON_PRETTY_PRINT));
}
/**
* @param string $path
*
* @return Backup
* @throws BackupFolderException
* @throws NotPermittedException
*/
private function parseFromFolder(string $path): Backup {
try {
$folder = $this->appData->getFolder($path);
$json = $folder->getFile(self::SUMMARY_FILE)
->getContent();
$summary = json_decode($json, true);
if (!is_array($summary)) {
throw new BackupFolderException();
}
$backup = new Backup();
$backup->setBaseFolder($folder);
return $backup->import($summary);
} catch (NotFoundException $e) {
throw new BackupFolderException();
}
}
/**
* @param Backup $backup
*/
private function addCustomAppsChunk(Backup $backup) {
// TODO: testing with custom apps folder (should work)
$customApps = $this->configService->getSystemValue('apps_paths');
if (is_array($customApps)) {
foreach ($customApps as $app) {
if (!is_array($app) || !array_key_exists('path', $app)) {
continue;
}
$backup->addChunk(
new BackupChunk(
BackupChunk::ROOT_DISK, $app['path'], 'apps_' . $this->uuid(8)
)
);
}
}
}
/**
* @param Backup $backup
*
* @throws ArchiveCreateExceptionAlias
* @throws Exception
*/
private function backupSql(Backup $backup) {
$content = $this->generateSqlDump();
$chunk = new BackupChunk(BackupChunk::SQL_DUMP, '', 'sqldump');
$this->archiveService->createContentArchive(
$backup, $chunk, self::SQL_DUMP_FILE, $content
);
$backup->addChunk($chunk);
}
/**
* @param Backup $backup
*
* @return string
* @throws Exception
*/
private function generateSqlDump() {
$data = [
'dbname' => $this->configService->getSystemValue('dbname'),
'dbhost' => $this->configService->getSystemValue('dbhost'),
'dbport' => $this->configService->getSystemValue('dbport'),
'dbuser' => $this->configService->getSystemValue('dbuser'),
'dbpassword' => $this->configService->getSystemValue('dbpassword')
];
// $folder = $this->appData->getFolder('/' . $backup->getName());
// $sql = $folder->newFile(self::SQL_DUMP_FILE);
// $sql->putContent('');
//
$sqlDump = new SqlDumpMySQL();
$content = $sqlDump->export($data);
return $content;
}
/**
* @param Backup $backup
* @param string $name
*
* @return BackupChunk
* @throws ChunkNotFoundException
*/
public function getChunk(Backup $backup, string $name): BackupChunk {
foreach ($backup->getChunks() as $chunk) {
if ($chunk->getName() === $name) {
return $chunk;
}
}
throw new ChunkNotFoundException();
}
}

505
lib/Service/CliService.php Normal file
Просмотреть файл

@ -0,0 +1,505 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Service;
use daita\MySmallPhpTools\Exceptions\MalformedArrayException;
use daita\MySmallPhpTools\Traits\TArrayTools;
use Exception;
use OCA\Backup\Exceptions\ArchiveDeleteException;
use OCA\Backup\Exceptions\ArchiveNotFoundException;
use OCA\Backup\Exceptions\BackupFolderException;
use OCA\Backup\Exceptions\ChunkNotFoundException;
use OCA\Backup\Exceptions\EncryptionKeyException;
use OCA\Backup\Model\ArchiveFile;
use OCA\Backup\Model\Backup;
use OCA\Backup\Model\BackupArchive;
use OCA\Backup\Model\BackupChunk;
use OCA\Backup\Model\BackupOptions;
use OCA\Backup\SqlDump\SqlDumpMySQL;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
/**
* Class CliService
*
* @package OCA\Backup\Service
*/
class CliService {
use TArrayTools;
/** @var ArchiveService */
private $archiveService;
/** @var BackupService */
private $backupService;
/** @var MiscService */
private $miscService;
/** @var InputInterface */
private $input;
/** @var OutputInterface */
private $output;
/**
* CliService constructor.
*
* @param ArchiveService $archiveService
* @param BackupService $backupService
* @param MiscService $miscService
*/
public function __construct(
ArchiveService $archiveService, BackupService $backupService, MiscService $miscService
) {
$this->archiveService = $archiveService;
$this->backupService = $backupService;
$this->miscService = $miscService;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*/
public function init(InputInterface $input, OutputInterface $output): void {
$this->input = $input;
$this->output = $output;
}
/**
* @param Backup $backup
*/
public function displayBackupResume(Backup $backup): void {
$version = $backup->getVersion();
array_pop($version);
$this->output->writeln('<info>Token:</info> ' . $backup->getId());
$this->output->writeln('<info>Name:</info> ' . $backup->getName());
$this->output->writeln('<info>Date:</info> ' . date('Y-m-d H:i:s', $backup->getCreation()));
$this->output->writeln('<info>Nextcloud</info> ' . implode('.', $version));
$root = $this->backupService->getAbsoluteRoot($backup);
$options = $backup->getOptions();
$root .= ($options->getNewRoot() !== '') ? ' -> ' . $options->getNewRoot() : '';
$this->output->writeln('<info>Root:</info> ' . $root);
$this->output->writeln('');
}
/**
* @param Backup $backup
*
* @throws EncryptionKeyException
* @throws ArchiveDeleteException
*/
public function displayBackupDetails(Backup $backup): void {
foreach ($backup->getChunks(true) as $backupChunk) {
$this->displayBackupChunk($backup, $backupChunk);
$this->displayBackupArchives($backup, $backupChunk->getArchives());
$this->output->writeln('');
}
}
/**
* @param Backup $backup
* @param BackupChunk $chunk
*/
public function displayBackupChunk(Backup $backup, BackupChunk $chunk): void {
$root = $chunk->getRoot();
// $options = $backup->getOptions();
if ($backup->getOptions()
->getNewRoot()) {
$root .= ' -> ' . $this->backupService->getExtractRoot($backup, $chunk);
}
$path = ($chunk->getPath() === '') ? '.' : $chunk->getPath();
$this->output->writeln('- <info>Chunk:</info> ' . $chunk->getName());
if ($chunk->getRoot() === '') {
return;
}
$this->output->writeln('- <info>Root:</info> ' . $root);
$this->output->writeln('- <info>Path:</info> ' . $path);
}
/**
* @param Backup $backup
*
* @throws ArchiveDeleteException
* @throws ArchiveNotFoundException
* @throws EncryptionKeyException
* @throws BackupFolderException
*/
public function displayBackupRestore(Backup $backup): void {
$options = $backup->getOptions();
foreach ($backup->getChunks(true) as $chunk) {
$this->displayBackupChunk($backup, $chunk);
// TODO: pre-manage archives (continue on Exception)
if ($chunk->getType() >= BackupChunk::SQL_DUMP) {
$this->manageNonFileBackup($backup, $chunk);
continue;
}
$root = $this->backupService->getExtractRoot($backup, $chunk);
$helper = new QuestionHelper();
$question = '- Extract this chunk in <info>' . $root . '</info> [Y/n]?';
if (!$helper->ask($this->input, $this->output, new ConfirmationQuestion($question))) {
continue;
}
foreach ($chunk->getArchives() as $archive) {
$this->output->write(' > ' . $archive->getName());
if ($options->isAll()) {
$this->archiveService->extractAll($backup, $archive, $root);
continue;
}
$this->archiveService->decryptArchive($backup, $archive);
$zip = $this->archiveService->openZipArchive($archive);
$this->archiveService->listFilesFromZip($archive, $zip);
foreach ($archive->getFiles() as $file) {
$this->output->write(' * ' . $file->getName(), false);
$this->archiveService->extractFilesFromZip($zip, $root, [$file]);
}
$this->archiveService->deleteArchive($backup, $archive, 'zip');
$this->output->writeln('');
}
if ($options->isAll()) {
$this->output->writeln('');
}
// TODO: post manage archives
if ($chunk->getType() === BackupChunk::FILE_CONFIG) {
$this->manageConfigBackup($backup, $chunk);
}
}
}
/**
* @param Backup $backup
*
* @throws ArchiveDeleteException
* @throws ArchiveNotFoundException
* @throws EncryptionKeyException
*/
public function displayFilesList(Backup $backup): void {
foreach ($backup->getChunks(true) as $backupChunk) {
$this->displayBackupChunk($backup, $backupChunk);
foreach ($backupChunk->getArchives() as $archive) {
$this->archiveService->decryptArchive($backup, $archive);
$zip = $this->archiveService->openZipArchive($archive);
$this->archiveService->listFilesFromZip($archive, $zip);
foreach ($archive->getFiles() as $file) {
if ($this->fitSearch($backup->getOptions(), $file)) {
echo '- ' . $archive->getName() . ': ' . $file->getName() . "\n";
}
}
$this->archiveService->deleteArchive($backup, $archive, 'zip');
}
}
}
/**
* @param BackupOptions $options
* @param ArchiveFile $file
*
* @return bool
*/
private function fitSearch(BackupOptions $options, ArchiveFile $file) {
$fitSearch = $fitPath = false;
$search = $options->getSearch();
$path = $options->getPath();
if ($search === '' || strpos($file->getName(), $search)) {
$fitSearch = true;
}
if ($path === '' || strpos($file->getName(), $path) === 0) {
$fitPath = true;
}
return ($fitPath && $fitSearch);
}
/**
* @param Backup $backup
* @param array $backupArchives
*
* @throws EncryptionKeyException
* @throws ArchiveDeleteException
*/
private function displayBackupArchives(Backup $backup, array $backupArchives): void {
$table = new Table($this->output);
$table->setHeaders(
['', 'Name', 'Files', 'Size', 'Encrypted Checksum', 'Archive Checksum'], []
);
$table->render();
$this->output->writeln('');
$c = 0;
foreach ($backupArchives as $archive) {
$checksum = $this->generateEncryptedChecksum($backup, $archive);
$decryptedChecksum = $this->generateArchiveChecksum($backup, $archive);
$table->appendRow(
[
++$c . '/' . count($backupArchives),
$archive->getName(),
$archive->getCount(),
$archive->getSize(),
$checksum,
$decryptedChecksum
]
);
}
}
/**
* @param Backup $backup
* @param BackupArchive $archive
*
* @return string
*/
private function generateEncryptedChecksum(Backup $backup, BackupArchive $archive): string {
if (!$this->input->getOption('check')) {
return $archive->getEncryptedChecksum();
}
try {
if ($this->archiveService->verifyChecksum($backup, $archive, true)) {
return '<info>' . $archive->getEncryptedChecksum() . '</info>';
}
return '<error>' . $archive->getEncryptedChecksum() . '</error>';
} catch (Exception $e) {
return '<error>' . $e->getMessage() . '</error>';
}
}
/**
* @param Backup $backup
* @param BackupArchive $archive
*
* @return string
* @throws EncryptionKeyException
* @throws ArchiveDeleteException
*/
private function generateArchiveChecksum(Backup $backup, BackupArchive $archive): string {
if ($backup->getEncryptionKey() === '') {
return $archive->getChecksum();
}
try {
$this->archiveService->decryptArchive($backup, $archive);
$check = $this->archiveService->verifyChecksum($backup, $archive, false);
$this->archiveService->deleteArchive($backup, $archive, 'zip');
if ($check) {
return '<info>' . $archive->getChecksum() . '</info>';
}
return '<error>' . $archive->getChecksum() . '</error>';
} catch (ArchiveNotFoundException $e) {
return '<error>' . $e->getMessage() . '</error>';
}
}
/**
* @param Backup $backup
* @param BackupChunk $chunk
*/
private function manageNonFileBackup(Backup $backup, BackupChunk $chunk): void {
switch ($chunk->getType()) {
case BackupChunk::SQL_DUMP:
$this->manageSqlDump($backup, $chunk);
break;
}
}
/**
* @param Backup $backup
* @param BackupChunk $chunk
*/
private function manageSqlDump(Backup $backup, BackupChunk $chunk) {
$helper = new QuestionHelper();
$data = [];
try {
$configChunk = $this->backupService->getChunk($backup, 'config');
$config = $this->backupService->getExtractRoot($backup, $configChunk) . 'config.php';
$question = '- Using database configured in <info>' . $config . '</info> [Y/n]?';
if (file_exists($config)
&& $helper->ask($this->input, $this->output, new ConfirmationQuestion($question))) {
$data = $this->extractDatabaseConfig($config);
} else {
$this->output->writeln(' <!> File <comment>' . $config . '</comment> not found');
}
} catch (ChunkNotFoundException | MalformedArrayException $e) {
}
if (empty($data)) {
// TODO: ask for credentials
}
$question = '- Restore your database in <info>' . $data['dbname'] . '</info> [Y/n]?';
if ($helper->ask($this->input, $this->output, new ConfirmationQuestion($question))) {
try {
$this->importDumpFromChunk($backup, $chunk, $data);
} catch (ArchiveDeleteException $e) {
} catch (ArchiveNotFoundException $e) {
} catch (EncryptionKeyException $e) {
}
}
}
/**
* @param string $config
*
* @return array
* @throws MalformedArrayException
*/
private function extractDatabaseConfig(string $config): array {
$CONFIG = [];
require($config);
$this->mustContains(['dbtype'], $CONFIG);
if ($CONFIG['dbtype'] === 'mysql') {
$this->mustContains(['dbname', 'dbport', 'dbhost', 'dbuser', 'dbpassword'], $CONFIG);
$data = [
'dbname' => $CONFIG['dbname'],
'dbhost' => $CONFIG['dbhost'],
'dbport' => $CONFIG['dbport'],
'dbuser' => $CONFIG['dbuser'],
'dbpassword' => $CONFIG['dbpassword']
];
return $data;
}
return [];
}
/**
* @param Backup $backup
* @param BackupChunk $chunk
*
* @param array $data
*
* @throws ArchiveDeleteException
* @throws ArchiveNotFoundException
* @throws EncryptionKeyException
*/
private function importDumpFromChunk(Backup $backup, BackupChunk $chunk, array $data) {
foreach ($chunk->getArchives() as $archive) {
$this->archiveService->decryptArchive($backup, $archive);
$zip = $this->archiveService->openZipArchive($archive);
$read =
$this->archiveService->extractContentFromZip($zip, BackupService::SQL_DUMP_FILE);
$sqlDump = new SqlDumpMySQL();
$sqlDump->import($data, $read);
$this->archiveService->deleteArchive($backup, $archive, 'zip');
}
}
/**
* @param Backup $backup
* @param BackupChunk $chunk
*/
private function manageConfigBackup(Backup $backup, BackupChunk $chunk) {
$options = $backup->getOptions();
if (empty($options->getConfig()) && !$options->isFixDataDir()) {
return;
}
$configFile = $this->backupService->getExtractRoot($backup, $chunk) . 'config.php';
$CONFIG = [];
require($configFile);
if ($options->isFixDataDir()) {
try {
$dataChunk = $this->backupService->getChunk($backup, 'data');
$CONFIG['datadirectory'] =
$this->backupService->getExtractRoot($backup, $dataChunk);
} catch (ChunkNotFoundException $e) {
}
}
$CONFIG = array_merge($CONFIG, $options->getConfig());
file_put_contents($configFile, "<?php\n" . '$CONFIG = ' . var_export($CONFIG, true) . ';');
}
}

Просмотреть файл

@ -0,0 +1,125 @@
<?php
/** @noinspection PhpUndefinedClassInspection */
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Service;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TPathTools;
use OCA\Backup\AppInfo\Application;
use OCP\IConfig;
class ConfigService {
use TPathTools;
use TArrayTools;
/** @var array */
public $defaults = [];
/** @var IConfig */
private $config;
/** @var MiscService */
private $miscService;
/**
* ConfigService constructor.
*
* @param IConfig $config
* @param MiscService $miscService
*/
public function __construct(IConfig $config, MiscService $miscService) {
$this->config = $config;
$this->miscService = $miscService;
}
/**
* Get a value by key
*
* @param string $key
*
* @return string
*/
public function getAppValue($key) {
$defaultValue = null;
if (array_key_exists($key, $this->defaults)) {
$defaultValue = $this->defaults[$key];
}
return $this->config->getAppValue(Application::APP_ID, $key, $defaultValue);
}
/**
* 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);
}
/**
* Set a value by key
*
* @param string $key
* @param string $value
*
* @return void
*/
public function setAppValue($key, $value) {
$this->config->setAppValue(Application::APP_ID, $key, $value);
}
/**
* @param $key
*
* @return mixed
*/
public function getSystemValue($key) {
return $this->config->getSystemValue($key, '');
}
}

Просмотреть файл

@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Service;
use OCA\Backup\Exceptions\ArchiveNotFoundException;
use OCA\Backup\Exceptions\EncryptionKeyException;
/**
* Class EncryptService
*
* @package OCA\Backup\Service
*/
class EncryptService {
const BLOCK_SIZE = 500;
/** @var MiscService */
private $miscService;
/**
* EncryptService constructor.
*
* @param MiscService $miscService
*/
public function __construct(MiscService $miscService) {
$this->miscService = $miscService;
}
/**
* @param resource $in
* @param resource $out
* @param string $key
*/
public function encryptFile($in, $out, $key) {
$iv = openssl_random_pseudo_bytes(16);
fwrite($out, $iv);
while (!feof($in)) {
$clear = fread($in, 16 * self::BLOCK_SIZE);
$encrypted = openssl_encrypt($clear, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
fwrite($out, $encrypted);
$iv = substr($encrypted, 0, 16);
}
fclose($in);
fclose($out);
}
/**
* @param resource $in
* @param resource $out
* @param string $key
*
* @throws ArchiveNotFoundException
* @throws EncryptionKeyException
*/
public function decryptFile($in, $out, $key) {
if (is_bool($in)) {
throw new ArchiveNotFoundException('archive not found');
}
$iv = fread($in, 16);
while (!feof($in)) {
$encrypted = fread($in, 16 * (self::BLOCK_SIZE + 1));
$clear = openssl_decrypt($encrypted, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
if (is_bool($clear)) {
throw new EncryptionKeyException('Wrong encryption key');
}
fwrite($out, $clear);
$iv = substr($encrypted, 0, 16);
}
fclose($in);
fclose($out);
}
}

Просмотреть файл

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Service;
use OCA\Backup\Model\Backup;
/**
* Class ExtractService
*
* @package OCA\Backup\Service
*/
class ExtractService {
const BLOCK_SIZE = 500;
/** @var MiscService */
private $miscService;
/**
* ExtractService constructor.
*
* @param MiscService $miscService
*/
public function __construct(MiscService $miscService) {
$this->miscService = $miscService;
}
/**
* @param $backup
*/
public function extract(Backup $backup) {
echo 'EXTRACT !';
echo 'EXTRACT !';
}
}

Просмотреть файл

@ -0,0 +1,164 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Service;
use daita\MySmallPhpTools\Traits\TArrayTools;
use daita\MySmallPhpTools\Traits\TPathTools;
use daita\MySmallPhpTools\Traits\TStringTools;
use OCA\Backup\Model\Backup;
use OCA\Backup\Model\BackupArchive;
use OCA\Backup\Model\BackupChunk;
/**
* Class FilesService
*
* @package OCA\Backup\Service
*/
class FilesService {
use TArrayTools;
use TStringTools;
const APP_ROOT = __DIR__ . '/../../';
/** @var ConfigService */
private $configService;
/** @var MiscService */
private $miscService;
/**
* FilesService constructor.
*
* @param ConfigService $configService
* @param MiscService $miscService
*/
public function __construct(ConfigService $configService, MiscService $miscService) {
$this->configService = $configService;
$this->miscService = $miscService;
}
/**
* @param BackupChunk $backupChunk
* @param string $path
*/
public function fillBackupChunk(BackupChunk $backupChunk, string $path = ''): void {
// TODO: check no trailing slashes
if (!is_dir($backupChunk->getAbsolutePath() . $path)) {
$backupChunk->addFile($path);
return;
}
if ($path !== '') {
$path .= '/';
}
if (file_exists($backupChunk->getAbsolutePath() . $path . BackupService::NOBACKUP_FILE)) {
return;
}
foreach (scandir($backupChunk->getAbsolutePath() . $path) as $entry) {
if ($entry === '.' || $entry === '..') {
continue;
}
$this->fillBackupChunk($backupChunk, $path . $entry);
}
}
use TPathTools;
/**
* @param BackupChunk $backupChunk
*/
public function initBackupChunk(BackupChunk $backupChunk): void {
$root = '';
switch ($backupChunk->getType()) {
case BackupChunk::ROOT_DISK:
$root = '/';
break;
case BackupChunk::ROOT_NEXTCLOUD:
$root = \OC::$SERVERROOT;
break;
case BackupChunk::ROOT_DATA:
$root = $this->configService->getSystemValue('datadirectory');
break;
case BackupChunk::FILE_CONFIG:
$root = \OC::$SERVERROOT;
$backupChunk->setPath('config/');
$backupChunk->setUniqueFile('config.php');
break;
}
if ($root !== '') {
$backupChunk->setRoot($this->withEndSlash($root));
}
}
/**
* @param string $path
*
* @return string[]
*/
public function getFilesFromApp($path = ''): array {
$files = [];
foreach (scandir(self::APP_ROOT . $path) as $entry) {
if ($entry === '.' || $entry === '..') {
continue;
}
if (is_dir(self::APP_ROOT . $path . $entry)) {
$files = array_merge($files, $this->getFilesFromApp($path . $entry . '/'));
}
if (is_file(self::APP_ROOT . $path . $entry)) {
$files[] = $path . $entry;
}
}
return $files;
}
}

Просмотреть файл

@ -0,0 +1,75 @@
<?php
/** @noinspection PhpUndefinedClassInspection */
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\Service;
use OCA\Backup\AppInfo\Application;
use OCP\ILogger;
/**
* Class MiscService
*
* @package OCA\Backup\Service
*/
class MiscService {
/** @var ILogger */
private $logger;
/**
* MiscService constructor.
*
* @param ILogger $logger
*/
public function __construct(ILogger $logger) {
$this->logger = $logger;
}
/**
* @param $message
* @param int $level
*/
public function log($message, $level = 2) {
$data = array(
'app' => Application::APP_ID,
'level' => $level
);
$this->logger->log($level, $message, $data);
}
}

Просмотреть файл

@ -0,0 +1,118 @@
<?php
declare(strict_types=1);
/**
* Nextcloud - Backup
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2019, Maxence Lange <maxence@artificial-owl.com>
* @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 <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Backup\SqlDump;
use Exception;
use Ifsnop\Mysqldump\Mysqldump;
use OCA\Backup\ISqlDump;
/**
* Class SqlDumpMySQL
*
* @package OCA\Backup\SqlDump
*/
class SqlDumpMySQL implements ISqlDump {
/**
* SqlDumpMySQL constructor.
*/
public function __construct() {
}
/**
* @param array $data
*
* @return string
* @throws Exception
*/
public function export(array $data): string {
$connect = 'mysql:host=' . $data['dbhost'] . ';dbname=' . $data['dbname'];
$settings = [
'compress' => Mysqldump::NONE,
'no-data' => false,
'add-drop-table' => true,
'single-transaction' => true,
'lock-tables' => true,
'add-locks' => true,
'extended-insert' => true,
'disable-foreign-keys-check' => true,
'skip-triggers' => false,
'add-drop-trigger' => true,
'databases' => false,
'add-drop-database' => true,
'hex-blob' => true
];
$dump = new Mysqldump($connect, $data['dbuser'], $data['dbpassword'], $settings);
ob_start();
$dump->start();
$content = ob_get_clean();
return $content;
}
/**
* @param array $data
* @param resource $read
*
* @return bool
*/
public function import(array $data, $read): bool {
$sql =
mysqli_connect($data['dbhost'], $data['dbuser'], $data['dbpassword'], $data['dbname']);
$request = '';
while (($line = fgets($read)) !== false) {
$line = trim($line);
if (substr($line, 0, 2) === '--' || $line === '') {
continue;
}
$request .= $line;
if (substr($line, -1) === ';') {
mysqli_query($sql, $request);
$request = '';
}
}
mysqli_close($sql);
return true;
}
}