Skip to content

Commit

Permalink
fix(push): Improve test-push output
Browse files Browse the repository at this point in the history
Signed-off-by: Joas Schilling <[email protected]>
  • Loading branch information
nickvergessen committed Feb 24, 2025
1 parent ba1f0c3 commit 7067024
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 24 deletions.
44 changes: 35 additions & 9 deletions lib/Command/TestPush.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ protected function configure(): void {
'talk',
null,
InputOption::VALUE_NONE,
'Test talk devices'
'Test Talk devices'
)
->addOption(
'files',
null,
InputOption::VALUE_NONE,
'Test other devices (Files, Notes, …)'
)
;
}
Expand All @@ -62,32 +68,52 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

$userId = $input->getArgument('user-id');
$subject = 'Testing push notifications';

$user = $this->userManager->get($userId);
if (!$user instanceof IUser) {
$output->writeln('Unknown user');
$output->writeln('<error>Unknown user</error>');
return 1;
}

$failed = false;
if ($input->getOption('talk')) {
$failed = $this->sendNotification($output, $user, 'talk') || $failed;

Check failure on line 79 in lib/Command/TestPush.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

TypeDoesNotContainType

lib/Command/TestPush.php:79:65: TypeDoesNotContainType: Operand of type false is always falsy (see https://psalm.dev/056)

Check failure on line 79 in lib/Command/TestPush.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis

TypeDoesNotContainType

lib/Command/TestPush.php:79:65: TypeDoesNotContainType: Type false for $failed is always !falsy (see https://psalm.dev/056)
}
if ($input->getOption('files')) {
$failed = $this->sendNotification($output, $user, 'files') || $failed;
}
if (!$input->getOption('talk') && !$input->getOption('files')) {
$failed = $this->sendNotification($output, $user, 'talk') || $failed;
$failed = $this->sendNotification($output, $user, 'files') || $failed;
}

return $failed ? 1 : 0;
}

protected function sendNotification(OutputInterface $output, IUser $user, string $clients): bool {
$app = $clients === 'talk' ? 'admin_notification_talk' : 'admin_notifications';
$notification = $this->notificationManager->createNotification();
$datetime = $this->timeFactory->getDateTime();
$app = $input->getOption('talk') ? 'admin_notification_talk' : 'admin_notifications';

$output->writeln('');
if ($clients === 'talk') {
$output->writeln('Testing Talk clients:');
} else {
$output->writeln('Testing other clients: Files, Notes, …');
}

try {
$notification->setApp($app)
->setUser($user->getUID())
->setDateTime($datetime)
->setObject('admin_notifications', dechex($datetime->getTimestamp()))
->setSubject('cli', [$subject]);
->setSubject('cli', ['Testing push notifications']);

$this->app->setOutput($output);
$this->notificationManager->notify($notification);
} catch (\InvalidArgumentException) {
$output->writeln('Error while sending the notification');
return 1;
return true;
}

return 0;
return false;
}
}
34 changes: 19 additions & 15 deletions lib/Push.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
use OCP\UserStatus\IUserStatus;
use OCP\Util;
use Psr\Log\LoggerInterface;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Console\Output\OutputInterface;

class Push {
Expand Down Expand Up @@ -237,13 +238,10 @@ public function pushToDevice(int $id, INotification $notification, ?OutputInterf
}

if (empty($devices)) {
$this->printInfo('No devices found for user');
$this->printInfo('<comment>No devices found for user</comment>');
return;
}

$this->printInfo('Trying to push to ' . count($devices) . ' devices');
$this->printInfo('');

if (!$notification->isValidParsed()) {
$language = $this->l10nFactory->getUserLanguage($user);
$this->printInfo('Language is set to ' . $language);
Expand All @@ -265,11 +263,16 @@ public function pushToDevice(int $id, INotification $notification, ?OutputInterf
$this->printInfo('Private user key size: ' . strlen($userKey->getPrivate()));
$this->printInfo('Public user key size: ' . strlen($userKey->getPublic()));


$this->printInfo('');
$this->printInfo('Found ' . count($devices) . ' devices registered for push notifications');
$isTalkNotification = \in_array($notification->getApp(), ['spreed', 'talk', 'admin_notification_talk'], true);
$devices = $this->filterDeviceList($devices, $notification->getApp());
if (empty($devices)) {
$this->printInfo('<comment>No devices left after filtering</comment>');
return;
}
$this->printInfo('Trying to push to ' . count($devices) . ' devices');

// We don't push to devices that are older than 60 days
$maxAge = time() - 60 * 24 * 60 * 60;
Expand Down Expand Up @@ -299,6 +302,7 @@ public function pushToDevice(int $id, INotification $notification, ?OutputInterf
$this->deletePushToken($device['token']);
}
}
$this->printInfo('');

if (!$this->deferPayloads) {
$this->sendNotificationsToProxies();
Expand Down Expand Up @@ -481,46 +485,46 @@ protected function sendNotificationsToProxies(): void {
'app' => 'notifications',
]);

$this->printInfo('Could not send notification to push server [' . $proxyServer . ']: ' . $error);
$this->printInfo('<error>Could not send notification to push server [' . $proxyServer . ']: ' . $error . '</error>');
continue;
} catch (\Exception $e) {
$this->log->error($e->getMessage(), [
'exception' => $e,
]);

$error = $e->getMessage() ?: 'no reason given';
$this->printInfo('Could not send notification to push server [' . $e::class . ']: ' . $error);
$this->printInfo('<error>Could not send notification to push server [' . $e::class . ']: ' . $error . '</error>');
continue;
}

if (is_array($bodyData) && array_key_exists('unknown', $bodyData) && array_key_exists('failed', $bodyData)) {
if (is_array($bodyData['unknown'])) {
// Proxy returns null when the array is empty
foreach ($bodyData['unknown'] as $unknownDevice) {
$this->printInfo('Deleting device because it is unknown by the push server: ' . $unknownDevice);
$this->printInfo('<comment>Deleting device because it is unknown by the push server: ' . $unknownDevice . '</comment>');
$this->deletePushTokenByDeviceIdentifier($unknownDevice);
}
}

if ($bodyData['failed'] !== 0) {
$this->printInfo('Push notification sent, but ' . $bodyData['failed'] . ' failed');
$this->printInfo('<comment>Push notification sent, but ' . $bodyData['failed'] . ' failed</comment>');
} else {
$this->printInfo('Push notification sent successfully');
$this->printInfo('<info>Push notification sent successfully</info>');
}
} elseif ($status !== Http::STATUS_OK) {
if ($status === Http::STATUS_TOO_MANY_REQUESTS) {
$this->config->setAppValue(Application::APP_ID, 'rate_limit_reached', (string)$this->timeFactory->getTime());
}
$error = $body && $bodyData === null ? $body : 'no reason given';
$this->printInfo('Could not send notification to push server [' . $proxyServer . ']: ' . $error);
$this->printInfo('<error>Could not send notification to push server [' . $proxyServer . ']: ' . $error . '</error>');
$this->log->warning('Could not send notification to push server [{url}]: {error}', [
'error' => $error,
'url' => $proxyServer,
'app' => 'notifications',
]);
} else {
$error = $body && $bodyData === null ? $body : 'no reason given';
$this->printInfo('Push notification sent but response was not parsable, using an outdated push proxy? [' . $proxyServer . ']: ' . $error);
$this->printInfo('<comment>Push notification sent but response was not parsable, using an outdated push proxy? [' . $proxyServer . ']: ' . $error . '</comment>');
$this->log->info('Push notification sent but response was not parsable, using an outdated push proxy? [{url}]: {error}', [
'error' => $error,
'url' => $proxyServer,
Expand All @@ -541,7 +545,7 @@ protected function validateToken(int $tokenId, int $maxAge): bool {
$age = $token->getLastCheck();
} catch (InvalidTokenException) {
// Token does not exist anymore, should drop the push device entry
$this->printInfo('InvalidTokenException is thrown');
$this->printInfo('<error>InvalidTokenException is thrown</error>');
$this->deletePushToken($tokenId);
$this->cache->set('t' . $tokenId, 0, 600);
return false;
Expand All @@ -553,7 +557,7 @@ protected function validateToken(int $tokenId, int $maxAge): bool {
return true;
}

$this->printInfo('Device token "last checked" is older than 60 days: ' . $age);
$this->printInfo('<comment>Device token "last checked" is older than 60 days: ' . $age . '</comment>');
return false;
}

Expand Down Expand Up @@ -602,14 +606,14 @@ protected function encryptAndSign(Key $userKey, array $device, int $id, INotific
if (!openssl_public_encrypt(json_encode($data), $encryptedSubject, $device['devicepublickey'], OPENSSL_PKCS1_PADDING)) {
$error = openssl_error_string();
$this->log->error($error, ['app' => 'notifications']);
$this->printInfo('Error while encrypting data: "' . $error . '"');
$this->printInfo('<error>Error while encrypting data: "' . $error . '"</error>');
throw new \InvalidArgumentException('Failed to encrypt message for device');
}

if (openssl_sign($encryptedSubject, $signature, $userKey->getPrivate(), OPENSSL_ALGO_SHA512)) {
$this->printInfo('Signed encrypted push subject');
} else {
$this->printInfo('Failed to signed encrypted push subject');
$this->printInfo('<error>Failed to signed encrypted push subject</error>');
}
$base64EncryptedSubject = base64_encode($encryptedSubject);
$base64Signature = base64_encode($signature);
Expand Down

0 comments on commit 7067024

Please sign in to comment.