diff --git a/lib/Dav/Faces/FaceRoot.php b/lib/Dav/Faces/FaceRoot.php index b78d1ff51..f4e68ecca 100644 --- a/lib/Dav/Faces/FaceRoot.php +++ b/lib/Dav/Faces/FaceRoot.php @@ -12,6 +12,8 @@ use OCA\Recognize\Db\FaceDetection; use OCA\Recognize\Db\FaceDetectionMapper; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\DB\Exception; use OCP\Files\IRootFolder; use OCP\IPreview; use OCP\ITagManager; @@ -57,10 +59,12 @@ public function getName() { */ public function setName($name) { try { - $this->clusterMapper->findByUserAndTitle($this->user->getUID(), $name); + $this->clusterMapper->findByUserAndTitle($this->user->getUID(), basename($name)); throw new Forbidden('Not allowed to create duplicate names'); } catch (DoesNotExistException $e) { // pass + } catch (MultipleObjectsReturnedException|Exception $e) { + throw $e; } $this->cluster->setTitle(basename($name)); $this->clusterMapper->update($this->cluster); diff --git a/lib/Dav/Faces/FacesHome.php b/lib/Dav/Faces/FacesHome.php index 576a423f2..b0645328c 100644 --- a/lib/Dav/Faces/FacesHome.php +++ b/lib/Dav/Faces/FacesHome.php @@ -50,9 +50,15 @@ public function setName($name) { } public function createDirectory($name) { + if ($this->childExists(basename($name))) { + throw new Forbidden('Not allowed to create duplicate names'); + } + if (preg_match('/^[0-9]+$/', basename($name), $matches) != false) { + throw new Forbidden('Not allowed to use numbers as names'); + } $entity = new FaceCluster(); $entity->setUserId($this->user->getUID()); - $entity->setTitle($name); + $entity->setTitle(basename($name)); $this->faceClusterMapper->insert($entity); } @@ -90,9 +96,9 @@ public function getChild($name) : FaceRoot { * @throws \OCP\DB\Exception */ public function getChildren(): array { - $clusters = $this->faceClusterMapper->findByUserId($this->user->getUID()); - $clusters = array_values(array_filter($clusters, fn ($cluster) => count($this->faceDetectionMapper->findByClusterId($cluster->getId())) > 0)); if (count($this->children) === 0) { + $clusters = $this->faceClusterMapper->findByUserId($this->user->getUID()); + $clusters = array_values(array_filter($clusters, fn ($cluster) => count($this->faceDetectionMapper->findByClusterId($cluster->getId())) > 0)); $this->children = array_map(function (FaceCluster $cluster) { return new FaceRoot($this->faceClusterMapper, $cluster, $this->user, $this->faceDetectionMapper, $this->rootFolder, $this->tagManager, $this->previewManager); }, $clusters); diff --git a/lib/Dav/Faces/PropFindPlugin.php b/lib/Dav/Faces/PropFindPlugin.php index 14a0fa4bf..76f771cbd 100644 --- a/lib/Dav/Faces/PropFindPlugin.php +++ b/lib/Dav/Faces/PropFindPlugin.php @@ -10,10 +10,15 @@ use \OCA\DAV\Connector\Sabre\FilesPlugin; use \OCA\DAV\Connector\Sabre\TagsPlugin; use OCA\DAV\Connector\Sabre\File; +use OCA\Recognize\Db\FaceClusterMapper; use OCA\Recognize\Db\FaceDetectionMapper; use OCA\Recognize\Db\FaceDetectionWithTitle; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\MultipleObjectsReturnedException; +use OCP\DB\Exception; use OCP\Files\DavUtil; use OCP\IPreview; +use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\INode; use Sabre\DAV\PropFind; use Sabre\DAV\Server; @@ -31,6 +36,7 @@ final class PropFindPlugin extends ServerPlugin { public function __construct( private FaceDetectionMapper $faceDetectionMapper, private IPreview $previewManager, + private FaceClusterMapper $faceClusterMapper, ) { } @@ -38,6 +44,7 @@ public function initialize(Server $server) { $this->server = $server; $this->server->on('propFind', [$this, 'propFind']); + $this->server->on('beforeMove', [$this, 'beforeMove']); } @@ -87,4 +94,23 @@ public function propFind(PropFind $propFind, INode $node) { }); } } + + public function beforeMove($source, $target) { + // recognize/{userId}/faces/{name} + if (str_starts_with($source, 'recognize') && str_starts_with($target, 'recognize')) { + $sourceParts = explode('/', $source); + $targetParts = explode('/', $target); + if ($sourceParts[2] === 'faces' && $targetParts[2] === 'faces' && count($sourceParts) === 4 && count($targetParts) === 4) { + try { + $this->faceClusterMapper->findByUserAndTitle($targetParts[1], $targetParts[3]); + throw new Forbidden('The target node already exists and cannot be overwritten'); + } catch (DoesNotExistException $e) { + return true; + } catch (MultipleObjectsReturnedException|Exception $e) { + throw $e; + } + } + } + return true; + } } diff --git a/lib/Db/FaceClusterMapper.php b/lib/Db/FaceClusterMapper.php index b71be4d06..6206ad60c 100644 --- a/lib/Db/FaceClusterMapper.php +++ b/lib/Db/FaceClusterMapper.php @@ -58,14 +58,18 @@ public function findByUserAndTitle(string $userId, string $title) : Entity { $qb = $this->db->getQueryBuilder(); $qb->select(FaceCluster::$columns) ->from('recognize_face_clusters') - ->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId))) - ->andWhere($qb->expr()->orX( + ->where($qb->expr()->eq('user_id', $qb->createPositionalParameter($userId))); + if (preg_match('/^[0-9]+$/', $title, $matches) != false) { + $qb->andWhere($qb->expr()->orX( $qb->expr()->eq('title', $qb->createPositionalParameter($title)), $qb->expr()->andX( $qb->expr()->eq('title', $qb->createPositionalParameter('')), $qb->expr()->eq('id', $qb->createPositionalParameter((int) $title, IQueryBuilder::PARAM_INT)) ) )); + } else { + $qb->andWhere($qb->expr()->eq('title', $qb->createPositionalParameter($title))); + } return $this->findEntity($qb); }