From ad2bbeaf7677ce71e322cddc86f51641d24adfce 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 [skip ci] --- lib/BackgroundJobs/Launcher.php | 16 ++++++++--- lib/Operation.php | 47 ++++++++++++++++----------------- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/lib/BackgroundJobs/Launcher.php b/lib/BackgroundJobs/Launcher.php index 7930bbd..d88eea9 100644 --- a/lib/BackgroundJobs/Launcher.php +++ b/lib/BackgroundJobs/Launcher.php @@ -42,10 +42,18 @@ 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 { $view = new View(dirname($path)); $tmpFile = $view->toTmpFile(basename($path)); @@ -59,6 +67,8 @@ protected function run($argument): void { $command = str_replace('%f', escapeshellarg($tmpFile), $command); } + $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 b86c445..daccb04 100644 --- a/lib/Operation.php +++ b/lib/Operation.php @@ -151,23 +151,27 @@ public function onEvent(string $eventName, Event $event, IRuleMatcher $ruleMatch $matches = $ruleMatcher->getFlows(false); 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); @@ -177,20 +181,18 @@ 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, '%f')) { @@ -214,35 +216,32 @@ protected function buildCommand(string $template, Node $node, string $event, arr $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; } /**