From ff12bee21555683680e2325c2672bb25acd7f1d4 Mon Sep 17 00:00:00 2001 From: Benjamin Frueh Date: Fri, 25 Sep 2026 12:36:19 +0200 Subject: [PATCH] chore: streamline placeholder substitution Signed-off-by: Benjamin Frueh --- lib/BackgroundJobs/Launcher.php | 18 +++++++++--- lib/Operation.php | 49 ++++++++++++++++----------------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/lib/BackgroundJobs/Launcher.php b/lib/BackgroundJobs/Launcher.php index acae34a..41da81c 100644 --- a/lib/BackgroundJobs/Launcher.php +++ b/lib/BackgroundJobs/Launcher.php @@ -44,12 +44,20 @@ public function __construct(ITimeFactory $time, LoggerInterface $logger) { * @param mixed $argument */ protected function run($argument): void { - $command = (string)$argument['command']; + if (!isset($argument['template'])) { + $this->logger->error('Skipping job: template argument is missing', [ + 'app' => Application::APPID, + 'argument' => $argument + ]); + return; + } + + $template = (string)$argument['template']; + $replacements = is_array($argument['replacements'] ?? null) ? $argument['replacements'] : []; - if (strpos($command, '%f')) { - $path = isset($argument['path']) ? (string)$argument['path'] : ''; + if (isset($argument['path']) && strpos($template, '%f') !== false) { try { - $command = str_replace('%f', escapeshellarg($this->resolveLocalPath($path)), $command); + $replacements['%f'] = escapeshellarg($this->resolveLocalPath((string)$argument['path'])); } catch (Exception $e) { $this->logger->warning($e->getMessage(), [ 'app' => Application::APPID, @@ -59,6 +67,8 @@ protected function run($argument): void { } } + $command = strtr($template, $replacements); + // with wrapping sh around the command, we leave any redirects intact, // but ensure that the script is not blocking Nextcloud's execution $wrapper = 'sh -c ' . escapeshellarg($command) . ' >/dev/null &'; diff --git a/lib/Operation.php b/lib/Operation.php index 719a784..167f5db 100644 --- a/lib/Operation.php +++ b/lib/Operation.php @@ -164,23 +164,27 @@ public function onEvent(string $eventName, Event $event, IRuleMatcher $ruleMatch } foreach ($matches as $match) { + $template = $match['operation']; try { - $command = $this->buildCommand($match['operation'], $node, $eventName, $extra); + $replacements = $this->buildReplacements($template, $node, $eventName, $extra); } catch (PlaceholderNotSubstituted $e) { $this->logger->warning( 'Could not substitute {placeholder} in {command} with node {node}', [ 'app' => Application::APPID, 'placeholder' => $e->getPlaceholder(), - 'command' => $match['operation'], + 'command' => $template, 'node' => $node, 'exception' => $e, ] ); continue; } - $args = ['command' => $command]; - if (strpos($command, '%f')) { + $args = [ + 'template' => $template, + 'replacements' => $replacements, + ]; + if (strpos($template, '%f') !== false) { $args['path'] = $node->getPath(); } $this->jobList->add(Launcher::class, $args); @@ -190,57 +194,52 @@ public function onEvent(string $eventName, Event $event, IRuleMatcher $ruleMatch } /** + * @return array * @throws PlaceholderNotSubstituted */ - protected function buildCommand(string $template, Node $node, string $event, array $extra = []): string { - $command = $template; + protected function buildReplacements(string $template, Node $node, string $event, array $extra = []): array { + $replacements = []; - if (strpos($command, '%e')) { - $command = str_replace('%e', escapeshellarg($event), $command); + if (strpos($template, '%e') !== false) { + $replacements['%e'] = escapeshellarg($event); } - if (strpos($command, '%n')) { - // Nextcloud relative-path - $ncRelPath = $this->replacePlaceholderN($node); - $command = str_replace('%n', escapeshellarg($ncRelPath), $command); - unset($ncRelPath); + if (strpos($template, '%n') !== false) { + $replacements['%n'] = escapeshellarg($this->replacePlaceholderN($node)); } - if (strpos($command, '%i')) { + if (strpos($template, '%i') !== false) { $nodeID = -1; try { $nodeID = $node->getId(); } catch (InvalidPathException|NotFoundException) { } - $command = str_replace('%i', escapeshellarg((string)$nodeID), $command); + $replacements['%i'] = escapeshellarg((string)$nodeID); } - if (strpos($command, '%a')) { + if (strpos($template, '%a') !== false) { $user = $this->session->getUser(); $userID = ''; if ($user instanceof IUser) { $userID = $user->getUID(); } - $command = str_replace('%a', escapeshellarg($userID), $command); + $replacements['%a'] = escapeshellarg($userID); } - if (strpos($command, '%o')) { + if (strpos($template, '%o') !== false) { $user = $node->getOwner(); $userID = ''; if ($user instanceof IUser) { $userID = $user->getUID(); } - $command = str_replace('%o', escapeshellarg($userID), $command); + $replacements['%o'] = escapeshellarg($userID); } - if (strpos($command, '%x')) { - if (!isset($extra['oldFilePath'])) { - $extra['oldFilePath'] = ''; - } - $command = str_replace('%x', escapeshellarg($extra['oldFilePath']), $command); + if (strpos($template, '%x') !== false) { + $replacements['%x'] = escapeshellarg($extra['oldFilePath'] ?? ''); } - return $command; + return $replacements; } /**