diff --git a/AUTHORS b/AUTHORS index fe478401fddb4..ae990e7abb602 100644 --- a/AUTHORS +++ b/AUTHORS @@ -187,6 +187,7 @@ - Individual IT Services - Informatyka Boguslawski sp. z o.o. sp.k., http://www.ib.pl/ - Iscle + - Israel Saba - J0WI - Jaakko Salo - Jacob Neplokh diff --git a/apps/dav/lib/Files/FileSearchBackend.php b/apps/dav/lib/Files/FileSearchBackend.php index eb548bbd55c0d..818d05268f4f6 100644 --- a/apps/dav/lib/Files/FileSearchBackend.php +++ b/apps/dav/lib/Files/FileSearchBackend.php @@ -482,7 +482,11 @@ private function castValue(SearchPropertyDefinition $property, $value) { case SearchPropertyDefinition::DATATYPE_DECIMAL: case SearchPropertyDefinition::DATATYPE_INTEGER: case SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER: - return 0 + $value; + if (is_int($value) || is_float($value)) { + return 0 + $value; + } + $v = filter_var($value, FILTER_VALIDATE_FLOAT); + return $v === false ? 0 : $v; case SearchPropertyDefinition::DATATYPE_DATETIME: if (is_numeric($value)) { return max(0, 0 + $value); diff --git a/apps/dav/tests/unit/Files/FileSearchBackendTest.php b/apps/dav/tests/unit/Files/FileSearchBackendTest.php index c6d6f85347bb7..ce8a68f401817 100644 --- a/apps/dav/tests/unit/Files/FileSearchBackendTest.php +++ b/apps/dav/tests/unit/Files/FileSearchBackendTest.php @@ -16,6 +16,7 @@ use OCA\DAV\Connector\Sabre\FilesPlugin; use OCA\DAV\Connector\Sabre\ObjectTree; use OCA\DAV\Connector\Sabre\Server; +use OCA\DAV\Connector\Sabre\TagsPlugin; use OCA\DAV\Files\FileSearchBackend; use OCP\Files\FileInfo; use OCP\Files\Folder; @@ -171,6 +172,85 @@ public function testSearchSize(): void { $this->assertEquals('/files/test/test/path', $result[0]->href); } + public function testNumericLiteralIntPassThrough(): void { + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + $receivedQuery = null; + $this->searchFolder + ->method('search') + ->willReturnCallback(function (ISearchQuery $query) use (&$receivedQuery) { + $receivedQuery = $query; + return [ + new \OC\Files\Node\File($this->rootFolder, $this->view, '/test/file'), + ]; + }); + + $query = $this->getBasicQuery(Operator::OPERATION_GREATER_THAN, FilesPlugin::SIZE_PROPERTYNAME, 10); + $this->search->search($query); + + $this->assertNotNull($receivedQuery); + $comparison = $receivedQuery->getSearchOperation(); + $this->assertInstanceOf(SearchComparison::class, $comparison); + $this->assertSame(ISearchComparison::COMPARE_GREATER_THAN, $comparison->getType()); + $this->assertSame('size', $comparison->getField()); + $this->assertSame(10, $comparison->getValue()); + } + + public function testNumericLiteralStringToFloat(): void { + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + $receivedQuery = null; + $this->searchFolder + ->method('search') + ->willReturnCallback(function (ISearchQuery $query) use (&$receivedQuery) { + $receivedQuery = $query; + return [ + new \OC\Files\Node\File($this->rootFolder, $this->view, '/test/file'), + ]; + }); + + $query = $this->getBasicQuery(Operator::OPERATION_GREATER_THAN, FilesPlugin::SIZE_PROPERTYNAME, '10.5'); + $this->search->search($query); + + $this->assertNotNull($receivedQuery); + $comparison = $receivedQuery->getSearchOperation(); + $this->assertInstanceOf(SearchComparison::class, $comparison); + $this->assertSame(ISearchComparison::COMPARE_GREATER_THAN, $comparison->getType()); + $this->assertSame('size', $comparison->getField()); + $this->assertSame(10, $comparison->getValue()); + } + + public function testNumericLiteralInvalidFallsBackToZero(): void { + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->willReturn($this->davFolder); + + /** @var ISearchQuery|null $receivedQuery */ + $receivedQuery = null; + $this->searchFolder + ->method('search') + ->willReturnCallback(function (ISearchQuery $query) use (&$receivedQuery) { + $receivedQuery = $query; + return [ + new \OC\Files\Node\File($this->rootFolder, $this->view, '/test/file'), + ]; + }); + + $query = $this->getBasicQuery(Operator::OPERATION_GREATER_THAN, FilesPlugin::SIZE_PROPERTYNAME, 'not-a-number'); + $this->search->search($query); + + $this->assertNotNull($receivedQuery); + $comparison = $receivedQuery->getSearchOperation(); + $this->assertInstanceOf(SearchComparison::class, $comparison); + $this->assertSame(ISearchComparison::COMPARE_GREATER_THAN, $comparison->getType()); + $this->assertSame('size', $comparison->getField()); + $this->assertSame(0, $comparison->getValue()); + } + public function testSearchMtime(): void { $this->tree->expects($this->any()) ->method('getNodeForPath') @@ -258,7 +338,7 @@ private function getBasicQuery(string $type, string $property, int|string|null $ } else { $where = new Operator( $type, - [new SearchPropertyDefinition($property, true, true, true), new Literal($value)] + [new SearchPropertyDefinition($property, true, true, true, $this->getDataTypeForProperty($property)), new Literal($value)] ); } $limit = new Limit(); @@ -266,6 +346,20 @@ private function getBasicQuery(string $type, string $property, int|string|null $ return new Query($select, $from, $where, $orderBy, $limit); } + private function getDataTypeForProperty(string $property): string { + switch ($property) { + case FilesPlugin::SIZE_PROPERTYNAME: + case FilesPlugin::INTERNAL_FILEID_PROPERTYNAME: + return SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER; + case '{DAV:}getlastmodified': + return SearchPropertyDefinition::DATATYPE_DATETIME; + case TagsPlugin::FAVORITE_PROPERTYNAME: + return SearchPropertyDefinition::DATATYPE_BOOLEAN; + default: + return SearchPropertyDefinition::DATATYPE_STRING; + } + } + public function testSearchNonFolder(): void { $this->expectException(\InvalidArgumentException::class);