diff --git a/apps/workflowengine/lib/Check/FileMimeType.php b/apps/workflowengine/lib/Check/FileMimeType.php index 42b93bd951367..991d7ebc739ed 100644 --- a/apps/workflowengine/lib/Check/FileMimeType.php +++ b/apps/workflowengine/lib/Check/FileMimeType.php @@ -26,6 +26,7 @@ */ namespace OCA\WorkflowEngine\Check; +use OC\Files\Storage\Local; use OCA\WorkflowEngine\Entity\File; use OCP\Files\IMimeTypeDetector; use OCP\Files\Storage\IStorage; @@ -76,7 +77,7 @@ public function setFileInfo(IStorage $storage, string $path, bool $isDir = false } /** - * The mimetype is only cached if the file exists. Otherwise files access + * The mimetype is only cached if the file has a valid mimetype. Otherwise files access * control will cache "application/octet-stream" for all the target node on: * rename, move, copy and all other methods which create a new item * @@ -91,7 +92,7 @@ public function setFileInfo(IStorage $storage, string $path, bool $isDir = false * @return string */ protected function cacheAndReturnMimeType(string $storageId, ?string $path, string $mimeType): string { - if ($path !== null && $this->storage->file_exists($path)) { + if ($path !== null && $mimeType !== 'application/octet-stream') { $this->mimeType[$storageId][$path] = $mimeType; } @@ -122,12 +123,15 @@ protected function getActualValue() { if ($this->mimeType[$this->storage->getId()][$this->path] !== null) { return $this->mimeType[$this->storage->getId()][$this->path]; } - - if ($this->storage->is_dir($this->path)) { - return $this->cacheAndReturnMimeType($this->storage->getId(), $this->path, 'httpd/unix-directory'); + $cacheEntry = $this->storage->getCache()->get($this->path); + if ($cacheEntry && $cacheEntry->getMimeType() !== 'application/octet-stream') { + return $this->cacheAndReturnMimeType($this->storage->getId(), $this->path, $cacheEntry->getMimeType()); } - if ($this->storage->file_exists($this->path) && $this->storage->filesize($this->path)) { + if ($this->storage->file_exists($this->path) && + $this->storage->filesize($this->path) && + $this->storage->instanceOfStorage(Local::class) + ) { $path = $this->storage->getLocalFile($this->path); $mimeType = $this->mimeTypeDetector->detectContent($path); return $this->cacheAndReturnMimeType($this->storage->getId(), $this->path, $mimeType); diff --git a/apps/workflowengine/tests/Check/FileMimeTypeTest.php b/apps/workflowengine/tests/Check/FileMimeTypeTest.php new file mode 100644 index 0000000000000..3ebcaa8f4b3a9 --- /dev/null +++ b/apps/workflowengine/tests/Check/FileMimeTypeTest.php @@ -0,0 +1,192 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\WorkflowEngine\Tests\Check; + +use OC\Files\Storage\Temporary; +use OCA\WorkflowEngine\Check\FileMimeType; +use OCP\Files\IMimeTypeDetector; +use OCP\IL10N; +use OCP\IRequest; +use Test\TestCase; + +class TemporaryNoLocal extends Temporary { + public function instanceOfStorage($className) { + if ($className === '\OC\Files\Storage\Local') { + return false; + } else { + return parent::instanceOfStorage($className); + } + } +} + +/** + * @group DB + */ +class FileMimeTypeTest extends TestCase { + /** @var IL10N */ + private $l10n; + /** @var IRequest */ + private $request; + /** @var IMimeTypeDetector */ + private $mimeDetector; + + private $extensions = [ + '.txt' => 'text/plain-path-detected', + ]; + + private $content = [ + 'text-content' => 'text/plain-content-detected', + ]; + + protected function setUp(): void { + parent::setUp(); + + $this->l10n = $this->createMock(IL10N::class); + $this->request = $this->createMock(IRequest::class); + $this->mimeDetector = $this->createMock(IMimeTypeDetector::class); + $this->mimeDetector->method('detectPath') + ->willReturnCallback(function ($path) { + foreach ($this->extensions as $extension => $mime) { + if (strpos($path, $extension) !== false) { + return $mime; + } + } + return 'application/octet-stream'; + }); + $this->mimeDetector->method('detectContent') + ->willReturnCallback(function ($path) { + $body = file_get_contents($path); + foreach ($this->content as $match => $mime) { + if (strpos($body, $match) !== false) { + return $mime; + } + } + return 'application/octet-stream'; + }); + } + + public function testUseCachedMimetype() { + $storage = new Temporary([]); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'asd'); + $storage->getScanner()->scan(''); + + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain')); + } + + public function testNonCachedNotExists() { + $storage = new Temporary([]); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-path-detected')); + } + + public function testNonCachedLocal() { + $storage = new Temporary([]); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'text-content'); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-content-detected')); + } + + public function testNonCachedNotLocal() { + $storage = new TemporaryNoLocal([]); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'text-content'); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-path-detected')); + } + + public function testFallback() { + $storage = new Temporary([]); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'unknown'); + + $this->assertTrue($check->executeCheck('is', 'application/octet-stream')); + } + + public function testFromCacheCached() { + $storage = new Temporary([]); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'asd'); + $storage->getScanner()->scan(''); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain')); + + $storage->getCache()->clear(); + + $this->assertTrue($check->executeCheck('is', 'text/plain')); + + $newCheck = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $newCheck->setFileInfo($storage, 'foo/bar.txt'); + $this->assertTrue($newCheck->executeCheck('is', 'text/plain-path-detected')); + } + + public function testExistsCached() { + $storage = new TemporaryNoLocal([]); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'text-content'); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-content-detected')); + $storage->unlink('foo/bar.txt'); + $this->assertTrue($check->executeCheck('is', 'text/plain-content-detected')); + + $newCheck = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $newCheck->setFileInfo($storage, 'foo/bar.txt'); + $this->assertTrue($newCheck->executeCheck('is', 'text/plain-path-detected')); + } + + public function testNonExistsNotCached() { + $storage = new TemporaryNoLocal([]); + + $check = new FileMimeType($this->l10n, $this->request, $this->mimeDetector); + $check->setFileInfo($storage, 'foo/bar.txt'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-path-detected')); + + $storage->mkdir('foo'); + $storage->file_put_contents('foo/bar.txt', 'text-content'); + + $this->assertTrue($check->executeCheck('is', 'text/plain-content-detected')); + } +}