diff --git a/.devcontainer/postCreateCommands.sh b/.devcontainer/postCreateCommands.sh
index 71959e0e..1195c417 100755
--- a/.devcontainer/postCreateCommands.sh
+++ b/.devcontainer/postCreateCommands.sh
@@ -1,5 +1,8 @@
#!/bin/bash
+bash -i -c 'nvm install 20'
+bash -i -c 'nvm use 20'
+
echo "setup php-scoper"
composer global require humbug/php-scoper
$(composer config home)/vendor/bin/php-scoper completion bash >> $HOME.bash_completion
@@ -22,10 +25,9 @@ echo ". /usr/share/bash-completion/bash_completion" >> /home/vscode/.bashrc
NEXTCLOUD_VERSION=$(grep -oP -m 1 "[0-9]+\.[0-9]+\.[0-9]+" install.sh)
mkdir -p ~/.ssh/
-ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
rm -rf nextcloud-server/
-git clone --depth 1 --recurse-submodules --single-branch --branch v$NEXTCLOUD_VERSION git@github.com:nextcloud/server.git ./nextcloud-server
+git clone --depth 1 --recurse-submodules --single-branch --branch v$NEXTCLOUD_VERSION https://github.com/nextcloud/server.git ./nextcloud-server
cd nextcloud-server
git submodule update --init
cd -
diff --git a/.vscode/launch.json b/.vscode/launch.json
index b78774f5..ddd9285f 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -11,7 +11,7 @@
"port": 9080,
"pathMappings": {
"/var/www/html/": "${workspaceFolder}/nextcloud-server",
- "/var/www/html/apps/gdatavaas": "${workspaceFolder}/",
+ "/var/www/html/apps/gdatavaas": "${workspaceFolder}/build/artifacts/gdatavaas",
},
"runtimeArgs": [
"-dxdebug.mode=debug",
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 74ae131a..02eac3c2 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,4 +6,5 @@
"[php]": {
"editor.defaultFormatter": "junstyle.php-cs-fixer"
},
+ "php.suggest.basic": false
}
\ No newline at end of file
diff --git a/Dockerfile.Nextcloud b/Dockerfile.Nextcloud
index bde95499..2a7de3cd 100644
--- a/Dockerfile.Nextcloud
+++ b/Dockerfile.Nextcloud
@@ -31,8 +31,6 @@ RUN echo "error_log = /var/www/html/data/php.log" >> "$PHP_INI_DIR/php.ini"
RUN sed -i 's/#LogLevel info ssl:warn/LogLevel debug/g' /etc/apache2/sites-available/000-default.conf
COPY xdebug.ini /tmp/xdebug.ini
-RUN if [[ "$INSTALL_XDEBUG" == "1" ]]; then \
- install-php-extensions gd xdebug; \
- mv /tmp/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; \
- fi
+RUN install-php-extensions gd xdebug;
+RUN mv /tmp/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini;
diff --git a/composer.json b/composer.json
index b9573f14..70ccdda1 100644
--- a/composer.json
+++ b/composer.json
@@ -22,6 +22,7 @@
"autoload": {
"psr-4": {
"OCP\\": "vendor/nextcloud/ocp/OCP",
+ "OCA\\Files_Trashbin\\": "nextcloud-server/apps/files_trashbin",
"OCA\\GDataVaas\\": "lib"
}
},
diff --git a/install.sh b/install.sh
index 31560fd8..a22cd709 100755
--- a/install.sh
+++ b/install.sh
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
-export NEXTCLOUD_VERSION=${1:-30.0.0}
+export NEXTCLOUD_VERSION=${1:-30.0.2}
export INSTALL_XDEBUG=${2:-1}
export XDEBUG_MODE=${XDEBUG_MODE:-develop}
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 29aff2de..741826ba 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -4,25 +4,16 @@
namespace OCA\GDataVaas\AppInfo;
-use OC\Files\Filesystem;
-use OCA\GDataVaas\AvirWrapper;
-use OCA\GDataVaas\CacheEntryListener;
use OCA\GDataVaas\Db\DbFileMapper;
-use OCA\GDataVaas\Service\MailService;
+use OCA\GDataVaas\EventListener\FileEventsListener;
use OCA\GDataVaas\Service\TagService;
-use OCA\GDataVaas\Service\VerdictService;
use OCA\GDataVaas\SystemTag\SystemTagObjectMapperWithoutActivityFactory;
-use OCP\Activity\IManager;
-use OCP\App\IAppManager;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent;
use OCP\EventDispatcher\IEventDispatcher;
-use OCP\Files\IHomeStorage;
-use OCP\Files\Storage\IStorage;
-use OCP\IAppConfig;
use OCP\IDBConnection;
use OCP\SystemTag\ISystemTagManager;
use OCP\SystemTag\ISystemTagObjectMapper;
@@ -43,6 +34,7 @@ public function __construct() {
$container = $this->getContainer();
$eventDispatcher = $container->get(IEventDispatcher::class);
+ assert($eventDispatcher instanceof IEventDispatcher);
$eventDispatcher->addListener(LoadAdditionalScriptsEvent::class, function () {
Util::addScript(self::APP_ID, 'gdatavaas-files-action');
});
@@ -53,9 +45,9 @@ public function __construct() {
* @return void
*/
public function register(IRegistrationContext $context): void {
- require_once file_exists(__DIR__.'/../../vendor/scoper-autoload.php')
- ? __DIR__.'/../../vendor/scoper-autoload.php'
- : __DIR__.'/../../vendor/autoload.php';
+ require_once file_exists(__DIR__ . '/../../vendor/scoper-autoload.php')
+ ? __DIR__ . '/../../vendor/scoper-autoload.php'
+ : __DIR__ . '/../../vendor/autoload.php';
// Manually register TagService so that we can customize the DI used for $silentTagMapper
$context->registerService(TagService::class, function ($c) {
@@ -69,50 +61,7 @@ public function register(IRegistrationContext $context): void {
return new TagService($logger, $systemTagManager, $standardTagMapper, $silentTagMapper, $dbFileMapper);
}, true);
- CacheEntryListener::register($context);
-
- // Util::connection is deprecated, but required ATM by FileSystem::addStorageWrapper
- Util::connectHook('OC_Filesystem', 'preSetup', $this, 'setupWrapper');
- }
-
- /**
- * * Add wrapper for local storages
- */
- public function setupWrapper(): void {
- Filesystem::addStorageWrapper(
- 'oc_gdata_vaas',
- function (string $mountPoint, IStorage $storage) {
- /*
- if ($storage->instanceOfStorage(Jail::class)) {
- // No reason to wrap jails again
- return $storage;
- }
- */
-
- $container = $this->getContainer();
- $verdictService = $container->get(VerdictService::class);
- $mailService = $container->get(MailService::class);
- $appConfig = $container->get(IAppConfig::class);
- // $l10n = $container->get(IL10N::class);
- $logger = $container->get(LoggerInterface::class);
- $activityManager = $container->get(IManager::class);
- $eventDispatcher = $container->get(IEventDispatcher::class);
- $appManager = $container->get(IAppManager::class);
- return new AvirWrapper([
- 'storage' => $storage,
- 'verdictService' => $verdictService,
- 'mailService' => $mailService,
- 'appConfig' => $appConfig,
- //'l10n' => $l10n,
- 'logger' => $logger,
- 'activityManager' => $activityManager,
- 'isHomeStorage' => $storage->instanceOfStorage(IHomeStorage::class),
- 'eventDispatcher' => $eventDispatcher,
- 'trashEnabled' => $appManager->isEnabledForUser('files_trashbin'),
- ]);
- },
- 1
- );
+ FileEventsListener::register($context);
}
public function boot(IBootContext $context): void {
diff --git a/lib/AvirWrapper.php b/lib/AvirWrapper.php
deleted file mode 100644
index fef698f5..00000000
--- a/lib/AvirWrapper.php
+++ /dev/null
@@ -1,238 +0,0 @@
-
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
- */
-
-namespace OCA\GDataVaas;
-
-use Coduo\PHPHumanizer\NumberHumanizer;
-use GuzzleHttp\Exception\ServerException;
-use OC\Files\Storage\Wrapper\Wrapper;
-use OCA\Files_Trashbin\Trash\ITrashManager;
-use OCA\GDataVaas\Activity\Provider;
-use OCA\GDataVaas\AppInfo\Application;
-use OCA\GDataVaas\Service\MailService;
-use OCA\GDataVaas\Service\VerdictService;
-use OCP\Activity\IManager as ActivityManager;
-use OCP\EventDispatcher\IEventDispatcher;
-use OCP\Files\EntityTooLargeException;
-use OCP\Files\InvalidContentException;
-use OCP\Files\NotFoundException;
-use OCP\Files\NotPermittedException;
-use OCP\IAppConfig;
-use OCP\IL10N;
-use Psr\Log\LoggerInterface;
-use VaasSdk\Exceptions\FileDoesNotExistException;
-use VaasSdk\Exceptions\InvalidSha256Exception;
-use VaasSdk\Exceptions\TimeoutException;
-use VaasSdk\Exceptions\UploadFailedException;
-use VaasSdk\Exceptions\VaasAuthenticationException;
-use VaasSdk\Message\Verdict;
-
-class AvirWrapper extends Wrapper {
- /**
- * Modes that are used for writing
- * @var array
- */
- private $writingModes = ['r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+'];
-
- protected VerdictService $verdictService;
- protected MailService $mailService;
- protected IAppConfig $appConfig;
-
- /** @var IL10N */
- protected $l10n;
-
- /** @var LoggerInterface */
- protected $logger;
-
- /** @var ActivityManager */
- protected $activityManager;
-
- /** @var bool */
- protected $isHomeStorage;
-
- /** @var bool */
- private $shouldScan = true;
-
- /** @var bool */
- private $trashEnabled;
-
- /**
- * @param array $parameters
- */
- public function __construct($parameters) {
- parent::__construct($parameters);
- $this->verdictService = $parameters['verdictService'];
- $this->mailService = $parameters['mailService'];
- $this->appConfig = $parameters['appConfig'];
- $this->logger = $parameters['logger'];
- $this->activityManager = $parameters['activityManager'];
- $this->isHomeStorage = $parameters['isHomeStorage'];
- $this->trashEnabled = $parameters['trashEnabled'];
-
- /** @var IEventDispatcher $eventDispatcher */
- $eventDispatcher = $parameters['eventDispatcher'];
- }
-
- /**
- * Asynchronously scan data that are written to the file
- * @param string $path
- * @param string $mode
- * @return resource | false
- */
- public function fopen($path, $mode) {
- $stream = $this->storage->fopen($path, $mode);
-
- /*
- * Only check when
- * - it is a resource
- * - it is a writing mode
- * - if it is a homestorage it starts with files/
- * - if it is not a homestorage we always wrap (external storages)
- */
- if ($this->shouldWrap($path) && is_resource($stream) && $this->isWritingMode($mode)) {
- $stream = $this->wrapSteam($path, $stream);
- }
- return $stream;
- }
-
- public function writeStream(string $path, $stream, ?int $size = null): int {
- if ($this->shouldWrap($path)) {
- $stream = $this->wrapSteam($path, $stream);
- }
- return parent::writeStream($path, $stream, $size);
- }
-
- public function rename($source, $target) {
- if ($this->shouldWrap($source)) {
- // After the upload apps/dav/lib/Connector/Sabre/File.php calls moveFromStorage which calls rename
- $this->logger->debug(sprintf('rename(%s, %s)', $source, $target));
- $this->verdictService->onRename($this->getLocalFile($source), $this->getLocalFile($target));
- }
- return parent::rename($source, $target);
- }
-
- private function shouldWrap(string $path): bool {
- return $this->shouldScan
- && (!$this->isHomeStorage
- || (strpos($path, 'files/') === 0
- || strpos($path, '/files/') === 0)
- );
- }
-
- private function wrapSteam(string $path, $stream) {
- try {
- $logger = $this->logger;
- return CallbackReadDataWrapper::wrap(
- $stream,
- null,
- null,
- function () use ($path, $logger) {
- $localPath = $this->getLocalFile($path);
- $filesize = $this->filesize($path);
- $logger->debug('Closing ' . $localPath . ' with size ' . $filesize);
-
- if (!$this->verdictService->isAllowedToScan($localPath)) {
- return;
- }
-
- if ($filesize > VerdictService::MAX_FILE_SIZE) {
- return;
- }
-
- try {
- $verdict = $this->verdictService->scan($localPath);
- } catch (EntityTooLargeException) {
- $this->logger->error("File $localPath is larger than " . NumberHumanizer::binarySuffix(VerdictService::MAX_FILE_SIZE, 'de'));
- } catch (FileDoesNotExistException) {
- $this->logger->error("File $localPath does not exist on upload");
- } catch (InvalidSha256Exception) {
- $this->logger->error("Invalid SHA256 for file $localPath on upload");
- } catch (NotFoundException) {
- $this->logger->error("File $localPath not found on upload");
- } catch (NotPermittedException) {
- $this->logger->error("Current settings do not permit scanning file $localPath on upload");
- } catch (TimeoutException) {
- $this->logger->error("Scanning timed out for file $localPath on upload");
- } catch (UploadFailedException|ServerException) {
- $this->logger->error("File $localPath could not be scanned on upload with GData VaaS because there was a temporary upstream server error");
- } catch (VaasAuthenticationException) {
- $this->logger->error('Authentication for VaaS scan failed. Please check your credentials.');
- } catch (\Exception $e) {
- $this->logger->error('Unexpected error while scanning file ' . $localPath . ' on upload: ' . $e->getMessage());
- }
- $logger->debug('Verdict for ' . $localPath . ' is ' . $verdict->Verdict->value);
-
- if ($verdict->Verdict == Verdict::MALICIOUS) {
- $logger->debug('Removing malicious file ' . $localPath);
-
- //prevent from going to trashbin
- if ($this->trashEnabled) {
- /** @var ITrashManager $trashManager */
- $trashManager = \OC::$server->query(ITrashManager::class);
- $trashManager->pauseTrash();
- }
-
- $owner = $this->getOwner($path);
- $this->unlink($path);
-
- if ($this->trashEnabled) {
- /** @var ITrashManager $trashManager */
- $trashManager = \OC::$server->query(ITrashManager::class);
- $trashManager->resumeTrash();
- }
-
- $this->logger->warning(
- 'Infected file deleted. ' . $verdict->Detection
- . ' Account: ' . $owner . ' Path: ' . $path,
- ['app' => 'gdatavaas']
- );
-
- $activity = $this->activityManager->generateEvent();
- $activity->setApp(Application::APP_ID)
- ->setSubject(Provider::SUBJECT_VIRUS_DETECTED_UPLOAD, [$verdict->Detection ?? 'no_detection_name'])
- ->setMessage(Provider::MESSAGE_FILE_DELETED)
- ->setObject('', 0, $path)
- ->setAffectedUser($owner)
- ->setType(Provider::TYPE_VIRUS_DETECTED);
- $this->activityManager->publish($activity);
-
- if ($this->appConfig->getValueBool(Application::APP_ID, 'sendMailOnVirusUpload')) {
- $this->mailService->notifyMaliciousUpload($verdict, $path, $owner, $filesize);
- }
-
- throw new InvalidContentException(
- sprintf(
- 'Virus %s is detected in the file. Upload cannot be completed.',
- $verdict->Detection
- )
- );
- }
- }
- );
- } catch (\Exception $e) {
- $this->logger->error($e->getMessage(), ['exception' => $e]);
- }
- return $stream;
- }
-
- /**
- * Checks whether passed mode is suitable for writing
- * @param string $mode
- * @return bool
- */
- private function isWritingMode($mode) {
- // Strip unessential binary/text flags
- $cleanMode = str_replace(
- ['t', 'b'],
- ['', ''],
- $mode
- );
- return in_array($cleanMode, $this->writingModes);
- }
-}
diff --git a/lib/CacheEntryListener.php b/lib/CacheEntryListener.php
deleted file mode 100644
index 5130e737..00000000
--- a/lib/CacheEntryListener.php
+++ /dev/null
@@ -1,54 +0,0 @@
-logger = $logger;
- $this->tagService = $tagService;
- $this->verdictService = $verdictService;
- }
-
- public static function register(IRegistrationContext $context): void {
- $context->registerEventListener(CacheEntryInsertedEvent::class, CacheEntryListener::class);
- $context->registerEventListener(CacheEntryUpdatedEvent::class, CacheEntryListener::class);
- }
-
- public function handle(Event $event): void {
- $this->logger->debug("CacheEntryListener");
- if (!$event instanceof AbstractCacheEvent) {
- return;
- }
-
- $storage = $event->getStorage();
- $path = $event->getPath();
- $fileId = $event->getFileId();
-
- $this->logger->debug("GotFields");
- if (self::shouldTag($path) && !$this->tagService->hasAnyVaasTag($fileId)) {
- $this->logger->debug("Handling " . get_class($event) . " for " . $path);
-
- $this->verdictService->tagLastScannedFile($storage->getLocalFile($path), $fileId);
- }
- }
-
- private static function shouldTag(string $path): bool {
- return str_starts_with($path, 'files/');
- }
-}
diff --git a/lib/CallbackReadDataWrapper.php b/lib/CallbackReadDataWrapper.php
deleted file mode 100644
index b854fe25..00000000
--- a/lib/CallbackReadDataWrapper.php
+++ /dev/null
@@ -1,60 +0,0 @@
- [
- 'source' => $source,
- 'readData' => $read,
- 'write' => $write,
- 'close' => $close,
- 'readDir' => $readDir,
- 'preClose' => $preClose
- ]
- ]);
- return Wrapper::wrapSource($source, $context, 'callbackReadData', self::class);
- }
-
- /**
- * @return true
- */
- protected function open() {
- $context = $this->loadContext('callbackReadData');
-
- $this->readDataCallback = $context['readData'];
- $this->writeCallback = $context['write'];
- $this->closeCallback = $context['close'];
- $this->readDirCallBack = $context['readDir'];
- return true;
- }
-
- public function stream_read($count) {
- $result = parent::stream_read($count);
- if (is_callable($this->readDataCallback)) {
- call_user_func($this->readDataCallback, strlen($result), $result);
- }
- return $result;
- }
-}
diff --git a/lib/EventListener/FileEventsListener.php b/lib/EventListener/FileEventsListener.php
new file mode 100644
index 00000000..f6f91aa5
--- /dev/null
+++ b/lib/EventListener/FileEventsListener.php
@@ -0,0 +1,117 @@
+ */
+class FileEventsListener implements IEventListener {
+ public function __construct(
+ private IUserSession $userSession,
+ private LoggerInterface $logger,
+ private IConfig $config,
+ private Server $server,
+ private IRequest $request,
+ private VerdictService $verdictService,
+ private FileService $fileService,
+ private TagService $tagService,
+ private IAppConfig $appConfig,
+ private MailService $mailService,
+ ) {
+ }
+
+ public static function register(IRegistrationContext $context): void {
+ $context->registerEventListener(NodeWrittenEvent::class, self::class);
+ }
+
+ public function handle(Event $event): void {
+ if ($event instanceof NodeWrittenEvent) {
+ $node = $event->getNode();
+ if ($node->getType() !== \OCP\Files\FileInfo::TYPE_FILE) {
+ return;
+ }
+ try {
+ $verdict = $this->verdictService->scanFileById($node->getId());
+ } catch (\Exception $e) {
+ $unscannedTagIsDisabled = $this->appConfig->getValueBool(Application::APP_ID, 'disableUnscannedTag');
+ if (!$unscannedTagIsDisabled) {
+ $this->tagService->setTag($node->getId(), TagService::UNSCANNED, silent: true);
+ }
+ $this->logger->error("Failed to scan uploaded file '{$node->getName()}' with ID '{$node->getId()}': {$e->getMessage()}");
+ return;
+ }
+
+ if ($verdict->Verdict->value == TagService::MALICIOUS) {
+ $this->sendErrorResponse(new VirusFoundException($verdict, $node->getName(), $node->getId()));
+ $this->fileService->deleteFile($node->getId());
+ if ($this->appConfig->getValueBool(Application::APP_ID, 'sendMailOnVirusUpload')) {
+ $this->mailService->notifyMaliciousUpload($verdict, $node->getPath(), $this->userSession->getUser()->getUID(), $node->getSize());
+ }
+ exit;
+ }
+ }
+ }
+
+ public function generateBody(Exception $ex): mixed {
+ if ($this->acceptHtml()) {
+ $templateName = 'exception';
+ $renderAs = 'guest';
+ $templateName = 'exception';
+ } else {
+ $templateName = 'xml_exception';
+ $renderAs = null;
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ }
+
+ $debug = $this->config->getSystemValueBool('debug', false);
+
+ $content = new OC_Template('gdatavaas', $templateName, $renderAs);
+ $content->assign('title', 'Error');
+ $content->assign('message', $ex->getMessage());
+ $content->assign('remoteAddr', $this->request->getRemoteAddress());
+ $content->assign('requestID', $this->request->getId());
+ $content->assign('debugMode', $debug);
+ $content->assign('errorClass', get_class($ex));
+ $content->assign('errorMsg', $ex->getMessage());
+ $content->assign('errorCode', $ex->getCode());
+ $content->assign('file', $ex->getFile());
+ $content->assign('line', $ex->getLine());
+ $content->assign('exception', $ex);
+ $contentString = $content->fetchPage();
+ return $contentString;
+ }
+
+ private function acceptHtml(): bool {
+ foreach (explode(',', $this->request->getHeader('Accept')) as $part) {
+ $subparts = explode(';', $part);
+ if (str_ends_with($subparts[0], '/html')) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private function sendErrorResponse(Exception $ex): void {
+ $this->server->httpResponse->setBody($this->generateBody($ex));
+ $this->server->httpResponse->setStatus(415);
+ $this->server->sapi->sendResponse($this->server->httpResponse);
+ }
+}
diff --git a/lib/Exceptions/VirusFoundException.php b/lib/Exceptions/VirusFoundException.php
new file mode 100644
index 00000000..f06c5a37
--- /dev/null
+++ b/lib/Exceptions/VirusFoundException.php
@@ -0,0 +1,14 @@
+Detection, 415);
+ }
+}
\ No newline at end of file
diff --git a/lib/Service/FileService.php b/lib/Service/FileService.php
index d3b53cbf..f0875a87 100644
--- a/lib/Service/FileService.php
+++ b/lib/Service/FileService.php
@@ -3,6 +3,8 @@
namespace OCA\GDataVaas\Service;
use OCA\GDataVaas\AppInfo\Application;
+use OCP\App\IAppManager;
+use OCA\Files_Trashbin\Trash\ITrashManager;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\InvalidPathException;
use OCP\Files\IRootFolder;
@@ -19,11 +21,15 @@ class FileService {
private IRootFolder $rootFolder;
private IAppConfig $appConfig;
private LoggerInterface $logger;
+ private IAppManager $appManager;
+ private ITrashManager $trashManager;
- public function __construct(LoggerInterface $logger, IUserMountCache $userMountCache, IRootFolder $rootFolder, IAppConfig $appConfig) {
+ public function __construct(LoggerInterface $logger, IUserMountCache $userMountCache, IRootFolder $rootFolder, IAppConfig $appConfig, IAppManager $appManager, ITrashManager $trashManager) {
$this->userMountCache = $userMountCache;
$this->rootFolder = $rootFolder;
$this->appConfig = $appConfig;
+ $this->appManager = $appManager;
+ $this->trashManager = $trashManager;
$this->logger = $logger;
}
@@ -101,4 +107,18 @@ public function moveFileToQuarantineFolderIfDefined(int $fileId): void {
$file->move($quarantine->getPath() . '/' . $file->getName());
$this->logger->info("File " . $file->getName() . " (" . $fileId . ") moved to quarantine folder.");
}
+
+ public function deleteFile(int $fileId): void {
+ $file = $this->getNodeFromFileId($fileId);
+ $file->unlock(\OCP\Lock\ILockingProvider::LOCK_SHARED);
+ $trashEnabled = $this->appManager->isEnabledForUser('files_trashbin');
+ if ($trashEnabled) {
+ $this->trashManager->pauseTrash();
+ }
+ $file->delete();
+ if ($trashEnabled) {
+ $this->trashManager->resumeTrash();
+ }
+ $this->logger->info("File " . $file->getName() . " (" . $fileId . ") deleted.");
+ }
}
diff --git a/scoper.inc.php b/scoper.inc.php
index 06443fad..262dfda6 100644
--- a/scoper.inc.php
+++ b/scoper.inc.php
@@ -19,7 +19,31 @@
// false,
// ),
// );
-$excludedFiles = ['templates/admin.php'];
+$excludedFiles = [
+ 'css/style.css',
+ 'LICENSES/AGPL-3.0-or-later.txt'
+];
+$excludedFolders = array_merge(
+ array_map(
+ static fn (SplFileInfo $fileInfo) => $fileInfo->getPathname(),
+ iterator_to_array(
+ Finder::create()
+ ->in('templates')
+ ->files(),
+ false,
+ ),
+ ),
+ array_map(
+ static fn (SplFileInfo $fileInfo) => $fileInfo->getPathname(),
+ iterator_to_array(
+ Finder::create()
+ ->in('src')
+ ->files(),
+ false,
+ ),
+));
+
+$excludedFiles = array_merge($excludedFiles, $excludedFolders);
return [
// The prefix configuration. If a non-null value is used, a random prefix
@@ -42,11 +66,27 @@
'finders' => [
Finder::create()
->files()
+ ->notName('babel.config.js')
+ ->notName('compose-install.yaml')
+ ->notName('composer.local.*')
+ ->notName('devcontainer.yaml')
+ ->notName('Dockerfile.Nextcloud')
+ ->notName('empty-skeleton.config.php')
+ ->notName('*.sh')
+ ->notName('Makefile')
+ ->notName('*.ini')
+ ->notName('psalm.xml')
+ ->notName('start-dev-environment*')
+ ->notName('scoper.inc.php')
+ ->notName('stylelint.config.js')
+ ->notName('use-*-vaas.sh')
+ ->notName('webpack.config.js')
+ ->notName('xdebug.*')
+ ->notName('babel.config.js')
->ignoreVCS(true)
->ignoreDotFiles(true)
->exclude([
'build',
- '.devcontainer',
'nextcloud-server',
'tests',
'tmp',
@@ -61,7 +101,6 @@
//
// For more see: https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#patchers
'exclude-files' => [
- // 'src/an-excluded-file.php',
...$excludedFiles,
],
@@ -106,10 +145,12 @@ static function (string $filePath, string $prefix, string $contents): string {
'OC\Files',
'OC\SystemTag',
'Symfony',
- 'Icewind'
+ 'Icewind',
+ 'Sabre\DAV'
],
'exclude-classes' => [
'OC',
+ 'OC_Template'
],
'exclude-functions' => [
],
diff --git a/templates/exception.php b/templates/exception.php
new file mode 100644
index 00000000..a6455963
--- /dev/null
+++ b/templates/exception.php
@@ -0,0 +1,50 @@
+');
+ p($e->getTraceAsString());
+ print_unescaped('');
+
+ if ($e->getPrevious() !== null) {
+ print_unescaped('
');
+ print_unescaped('
t($_['message'])) ?>
+ +