Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/notifications.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJoas Schilling <coding@schilljs.com>2019-04-10 17:01:43 +0300
committerJoas Schilling <coding@schilljs.com>2019-07-18 10:38:06 +0300
commit7efe105bdb1ef1e8e827048acd059e5d3a098e8e (patch)
tree3460498c59bbb5c5b981ce51ff38c2890703760d /lib
parent8c378b4e6adfa4b68288910b13c2c54e801a83c2 (diff)
Send a push message when a notification was deleted
Signed-off-by: Joas Schilling <coding@schilljs.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/App.php12
-rw-r--r--lib/AppInfo/Application.php29
-rw-r--r--lib/Capabilities.php1
-rw-r--r--lib/Controller/EndpointController.php21
-rw-r--r--lib/Handler.php27
-rw-r--r--lib/Notifier/AdminNotifications.php24
-rw-r--r--lib/Push.php113
7 files changed, 197 insertions, 30 deletions
diff --git a/lib/App.php b/lib/App.php
index 6be3dde..192e100 100644
--- a/lib/App.php
+++ b/lib/App.php
@@ -42,7 +42,7 @@ class App implements IApp {
* @throws \InvalidArgumentException When the notification is not valid
* @since 8.2.0
*/
- public function notify(INotification $notification) {
+ public function notify(INotification $notification): void {
$notificationId = $this->handler->add($notification);
try {
@@ -66,7 +66,13 @@ class App implements IApp {
* @param INotification $notification
* @since 8.2.0
*/
- public function markProcessed(INotification $notification) {
- $this->handler->delete($notification);
+ public function markProcessed(INotification $notification): void {
+ $deleted = $this->handler->delete($notification);
+
+ foreach ($deleted as $user => $notifications) {
+ foreach ($notifications as $notificationId) {
+ $this->push->pushDeleteToDevice($user, $notificationId);
+ }
+ }
}
}
diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php
index 0110423..e91a02a 100644
--- a/lib/AppInfo/Application.php
+++ b/lib/AppInfo/Application.php
@@ -41,31 +41,26 @@ class Application extends \OCP\AppFramework\App {
});
}
- public function register() {
+ public function register(): void {
$this->registerNotificationApp();
$this->registerAdminNotifications();
$this->registerUserInterface();
}
- protected function registerNotificationApp() {
- $container = $this->getContainer();
- $container->getServer()->getNotificationManager()->registerApp(function() use($container) {
- return $container->query(App::class);
- });
+ protected function registerNotificationApp(): void {
+ $this->getContainer()
+ ->getServer()
+ ->getNotificationManager()
+ ->registerApp(App::class);
}
- protected function registerAdminNotifications() {
- $this->getContainer()->getServer()->getNotificationManager()->registerNotifier(function() {
- return $this->getContainer()->query(AdminNotifications::class);
- }, function() {
- $l = $this->getContainer()->getServer()->getL10NFactory()->get('notifications');
- return [
- 'id' => 'admin_notifications',
- 'name' => $l->t('Admin notifications'),
- ];
- });
+ protected function registerAdminNotifications(): void {
+ $this->getContainer()
+ ->getServer()
+ ->getNotificationManager()
+ ->registerNotifier(AdminNotifications::class);
}
- protected function registerUserInterface() {
+ protected function registerUserInterface(): void {
// Only display the app on index.php except for public shares
$server = $this->getContainer()->getServer();
$request = $server->getRequest();
diff --git a/lib/Capabilities.php b/lib/Capabilities.php
index be654c1..6e19e90 100644
--- a/lib/Capabilities.php
+++ b/lib/Capabilities.php
@@ -49,6 +49,7 @@ class Capabilities implements ICapability {
'push' => [
'devices',
'object-data',
+ 'delete',
],
'admin-notifications' => [
'ocs',
diff --git a/lib/Controller/EndpointController.php b/lib/Controller/EndpointController.php
index 8eff06c..4f60c87 100644
--- a/lib/Controller/EndpointController.php
+++ b/lib/Controller/EndpointController.php
@@ -23,6 +23,7 @@ namespace OCA\Notifications\Controller;
use OCA\Notifications\Exceptions\NotificationNotFoundException;
use OCA\Notifications\Handler;
+use OCA\Notifications\Push;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
@@ -37,15 +38,15 @@ use OCP\Notification\INotification;
class EndpointController extends OCSController {
/** @var Handler */
private $handler;
-
/** @var IManager */
private $manager;
-
+ /** @var IConfig */
+ private $config;
/** @var IUserSession */
private $session;
+ /** @var Push */
+ private $push;
- /** @var IConfig */
- private $config;
/**
* @param string $appName
@@ -54,14 +55,22 @@ class EndpointController extends OCSController {
* @param IManager $manager
* @param IConfig $config
* @param IUserSession $session
+ * @param Push $push
*/
- public function __construct($appName, IRequest $request, Handler $handler, IManager $manager, IConfig $config, IUserSession $session) {
+ public function __construct(string $appName,
+ IRequest $request,
+ Handler $handler,
+ IManager $manager,
+ IConfig $config,
+ IUserSession $session,
+ Push $push) {
parent::__construct($appName, $request);
$this->handler = $handler;
$this->manager = $manager;
$this->config = $config;
$this->session = $session;
+ $this->push = $push;
}
/**
@@ -154,6 +163,7 @@ class EndpointController extends OCSController {
}
$this->handler->deleteById($id, $this->getCurrentUser());
+ $this->push->pushDeleteToDevice($this->getCurrentUser(), $id);
return new DataResponse();
}
@@ -164,6 +174,7 @@ class EndpointController extends OCSController {
*/
public function deleteAllNotifications(): DataResponse {
$this->handler->deleteByUser($this->getCurrentUser());
+ $this->push->pushDeleteToDevice($this->getCurrentUser(), 0);
return new DataResponse();
}
diff --git a/lib/Handler.php b/lib/Handler.php
index 863425b..d8c2451 100644
--- a/lib/Handler.php
+++ b/lib/Handler.php
@@ -84,12 +84,33 @@ class Handler {
* Delete the notifications matching the given Notification
*
* @param INotification $notification
+ * @return array A Map with all deleted notifications [user => [notifications]]
*/
- public function delete(INotification $notification) {
+ public function delete(INotification $notification): array {
$sql = $this->connection->getQueryBuilder();
- $sql->delete('notifications');
+ $sql->select('notification_id', 'user')
+ ->from('notifications');
+
$this->sqlWhere($sql, $notification);
- $sql->execute();
+ $statement = $sql->execute();
+
+ $deleted = [];
+ while ($row = $statement->fetch()) {
+ if (!isset($deleted[$row['user']])) {
+ $deleted[$row['user']] = [];
+ }
+
+ $deleted[$row['user']][] = (int) $row['notification_id'];
+ }
+ $statement->closeCursor();
+
+ foreach ($deleted as $user => $notifications) {
+ foreach ($notifications as $notificationId) {
+ $this->deleteById($notificationId, $user);
+ }
+ }
+
+ return $deleted;
}
/**
diff --git a/lib/Notifier/AdminNotifications.php b/lib/Notifier/AdminNotifications.php
index 1e63672..1de2bc3 100644
--- a/lib/Notifier/AdminNotifications.php
+++ b/lib/Notifier/AdminNotifications.php
@@ -25,6 +25,7 @@ namespace OCA\Notifications\Notifier;
use OCP\IURLGenerator;
use OCP\L10N\IFactory;
+use OCP\Notification\AlreadyProcessedException;
use OCP\Notification\INotification;
use OCP\Notification\INotifier;
@@ -46,12 +47,33 @@ class AdminNotifications implements INotifier {
}
/**
+ * Identifier of the notifier, only use [a-z0-9_]
+ *
+ * @return string
+ * @since 17.0.0
+ */
+ public function getID(): string {
+ return 'admin_notifications';
+ }
+
+ /**
+ * Human readable name describing the notifier
+ *
+ * @return string
+ * @since 17.0.0
+ */
+ public function getName(): string {
+ return $this->l10nFactory->get('notifications')->t('Admin notifications');
+ }
+
+ /**
* @param INotification $notification
* @param string $languageCode The code of the language that should be used to prepare the notification
* @return INotification
* @throws \InvalidArgumentException When the notification was not prepared by a notifier
+ * @throws AlreadyProcessedException When the notification is not needed anymore and should be deleted
*/
- public function prepare(INotification $notification, $languageCode): INotification {
+ public function prepare(INotification $notification, string $languageCode): INotification {
if ($notification->getApp() !== 'admin_notifications') {
throw new \InvalidArgumentException('Unknown app');
}
diff --git a/lib/Push.php b/lib/Push.php
index a9cca15..5b8c087 100644
--- a/lib/Push.php
+++ b/lib/Push.php
@@ -66,7 +66,7 @@ class Push {
$this->log = $log;
}
- public function pushToDevice(int $id, INotification $notification) {
+ public function pushToDevice(int $id, INotification $notification): void {
$user = $this->userManager->get($notification->getUser());
if (!($user instanceof IUser)) {
return;
@@ -170,6 +170,76 @@ class Push {
}
}
+ public function pushDeleteToDevice(string $userId, int $notificationId): void {
+ $user = $this->userManager->get($userId);
+ if (!($user instanceof IUser)) {
+ return;
+ }
+
+ $devices = $this->getDevicesForUser($userId);
+ if (empty($devices)) {
+ return;
+ }
+
+ $userKey = $this->keyManager->getKey($user);
+ $pushNotifications = [];
+ foreach ($devices as $device) {
+ try {
+ $payload = json_encode($this->encryptAndSignDelete($userKey, $device, $notificationId));
+
+ $proxyServer = rtrim($device['proxyserver'], '/');
+ if (!isset($pushNotifications[$proxyServer])) {
+ $pushNotifications[$proxyServer] = [];
+ }
+ $pushNotifications[$proxyServer][] = $payload;
+ } catch (InvalidTokenException $e) {
+ // Token does not exist anymore, should drop the push device entry
+ $this->deletePushToken($device['token']);
+ } catch (\InvalidArgumentException $e) {
+ // Failed to encrypt message for device: public key is invalid
+ $this->deletePushToken($device['token']);
+ }
+ }
+
+ if (empty($pushNotifications)) {
+ return;
+ }
+
+ $client = $this->clientService->newClient();
+ foreach ($pushNotifications as $proxyServer => $notifications) {
+ try {
+ $response = $client->post($proxyServer . '/notifications', [
+ 'body' => [
+ 'notifications' => $notifications,
+ ],
+ ]);
+ } catch (\Exception $e) {
+ $this->log->logException($e, [
+ 'app' => 'notifications',
+ 'level' => $e->getCode() === Http::STATUS_BAD_REQUEST ? ILogger::INFO : ILogger::WARN,
+ ]);
+ continue;
+ }
+
+ $status = $response->getStatusCode();
+ if ($status !== Http::STATUS_OK && $status !== Http::STATUS_SERVICE_UNAVAILABLE) {
+ $body = $response->getBody();
+ $this->log->error('Could not send notification to push server [{url}]: {error}',[
+ 'error' => \is_string($body) ? $body : 'no reason given',
+ 'url' => $proxyServer,
+ 'app' => 'notifications',
+ ]);
+ } else if ($status === Http::STATUS_SERVICE_UNAVAILABLE && $this->config->getSystemValue('debug', false)) {
+ $body = $response->getBody();
+ $this->log->debug('Could not send notification to push server [{url}]: {error}',[
+ 'error' => \is_string($body) ? $body : 'no reason given',
+ 'url' => $proxyServer,
+ 'app' => 'notifications',
+ ]);
+ }
+ }
+ }
+
/**
* @param Key $userKey
* @param array $device
@@ -224,6 +294,47 @@ class Push {
}
/**
+ * @param Key $userKey
+ * @param array $device
+ * @param int $id
+ * @return array
+ * @throws InvalidTokenException
+ * @throws \InvalidArgumentException
+ */
+ protected function encryptAndSignDelete(Key $userKey, array $device, int $id): array {
+ // Check if the token is still valid...
+ $this->tokenProvider->getTokenById($device['token']);
+
+ if ($id === 0) {
+ $data = [
+ 'delete-all' => true,
+ ];
+ } else {
+ $data = [
+ 'nid' => $id,
+ 'delete' => true,
+ ];
+ }
+
+ if (!openssl_public_encrypt(json_encode($data), $encryptedSubject, $device['devicepublickey'], OPENSSL_PKCS1_PADDING)) {
+ $this->log->error(openssl_error_string(), ['app' => 'notifications']);
+ throw new \InvalidArgumentException('Failed to encrypt message for device');
+ }
+
+ openssl_sign($encryptedSubject, $signature, $userKey->getPrivate(), OPENSSL_ALGO_SHA512);
+ $base64EncryptedSubject = base64_encode($encryptedSubject);
+ $base64Signature = base64_encode($signature);
+
+ return [
+ 'deviceIdentifier' => $device['deviceidentifier'],
+ 'pushTokenHash' => $device['pushtokenhash'],
+ 'subject' => $base64EncryptedSubject,
+ 'signature' => $base64Signature,
+ 'priority' => 'normal',
+ ];
+ }
+
+ /**
* @param string $uid
* @return array[]
*/