diff --git a/config/services.yaml b/config/services.yaml index 583afc5..b91e5ea 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -64,3 +64,5 @@ services: App\Controller\Absence\AbsenceJustificationDownloadController: arguments: $uploadDir: '%absence_justification_upload_dir%' + + App\Service\Share\FileSource: '@App\Service\Share\SmbFileSource' diff --git a/src/Service/Share/Exception/ShareConnectionException.php b/src/Service/Share/Exception/ShareConnectionException.php new file mode 100644 index 0000000..c3bb11d --- /dev/null +++ b/src/Service/Share/Exception/ShareConnectionException.php @@ -0,0 +1,9 @@ +requireUsableConfig(); + $share = $this->connect($config); + $full = $this->pathResolver->fullPath((string) $config->getBasePath(), $relativePath); + + try { + $infos = $share->dir($full); + } catch (Throwable $e) { + throw new ShareConnectionException($e->getMessage(), 0, $e); + } + + $entries = array_map(fn (IFileInfo $i): FileEntry => $this->toEntry($i, $relativePath), $infos); + + usort($entries, static function (FileEntry $a, FileEntry $b): int { + if ($a->isDir !== $b->isDir) { + return $a->isDir ? -1 : 1; + } + + return strcasecmp($a->name, $b->name); + }); + + return $entries; + } + + public function read(string $relativePath) + { + $config = $this->requireUsableConfig(); + $share = $this->connect($config); + $full = $this->pathResolver->fullPath((string) $config->getBasePath(), $relativePath); + + try { + return $share->read($full); + } catch (Throwable $e) { + throw new ShareConnectionException($e->getMessage(), 0, $e); + } + } + + public function test(): ShareTestResult + { + try { + $config = $this->requireUsableConfig(); + $share = $this->connect($config); + $share->dir($this->pathResolver->fullPath((string) $config->getBasePath(), '')); + + return new ShareTestResult(true); + } catch (ShareNotConfiguredException $e) { + return new ShareTestResult(false, 'Configuration incomplète ou désactivée.'); + } catch (Throwable $e) { + return new ShareTestResult(false, $e->getMessage()); + } + } + + private function requireUsableConfig(): ShareConfiguration + { + $config = $this->configRepository->findSingleton(); + + if (null === $config || !$config->isUsable()) { + throw new ShareNotConfiguredException('Share is not configured or disabled.'); + } + + return $config; + } + + private function connect(ShareConfiguration $config): IShare + { + $password = null !== $config->getEncryptedPassword() + ? $this->tokenEncryptor->decrypt($config->getEncryptedPassword()) + : ''; + + $auth = new BasicAuth( + (string) $config->getUsername(), + $config->getDomain() ?: 'WORKGROUP', + $password, + ); + $server = new ServerFactory()->createServer((string) $config->getHost(), $auth); + + try { + return $server->getShare((string) $config->getShareName()); + } catch (Throwable $e) { + throw new ShareConnectionException($e->getMessage(), 0, $e); + } + } + + private function toEntry(IFileInfo $info, string $parentRelative): FileEntry + { + $parent = '' === $parentRelative ? '' : rtrim($parentRelative, '/').'/'; + $path = $parent.$info->getName(); + $isDir = $info->isDirectory(); + + $mime = 'application/octet-stream'; + if (!$isDir) { + $guessed = MimeTypes::getDefault()->getMimeTypes(pathinfo($info->getName(), PATHINFO_EXTENSION)); + $mime = $guessed[0] ?? 'application/octet-stream'; + } + + return new FileEntry( + name: $info->getName(), + path: $path, + isDir: $isDir, + size: $isDir ? 0 : $info->getSize(), + modifiedAt: $info->getMTime(), + mimeType: $mime, + ); + } +}