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
diff options
context:
space:
mode:
authorJoas Schilling <coding@schilljs.com>2017-11-22 14:55:07 +0300
committerGitHub <noreply@github.com>2017-11-22 14:55:07 +0300
commit0cc01a3e149395c69e4a4f066ec53c3478e7840e (patch)
treea1e0d3b67731b37b652291affcfa20c22a3d9109
parentc77d4850075ebe98aa9e8d11a8f4f288a374b486 (diff)
parentdeba129d29d636da36ad204d3324cf33b44bb38d (diff)
Merge pull request #99 from nextcloud/talk-special-handling
Talk special handling
-rwxr-xr-xappinfo/database.xml6
-rw-r--r--appinfo/info.xml2
-rw-r--r--lib/Controller/PushController.php30
-rw-r--r--lib/Push.php32
-rw-r--r--tests/Unit/PushTest.php130
5 files changed, 191 insertions, 9 deletions
diff --git a/appinfo/database.xml b/appinfo/database.xml
index 07febf9..0eca938 100755
--- a/appinfo/database.xml
+++ b/appinfo/database.xml
@@ -166,6 +166,12 @@
<notnull>true</notnull>
<length>256</length>
</field>
+ <field>
+ <name>apptype</name>
+ <type>text</type>
+ <notnull>true</notnull>
+ <length>32</length>
+ </field>
<index>
<name>oc_notifpushtoken</name>
diff --git a/appinfo/info.xml b/appinfo/info.xml
index 25953e9..aa07848 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -15,7 +15,7 @@
<licence>AGPL</licence>
<author>Joas Schilling</author>
- <version>2.1.0</version>
+ <version>2.1.1</version>
<types>
<logging/>
diff --git a/lib/Controller/PushController.php b/lib/Controller/PushController.php
index aef6eb3..36d0237 100644
--- a/lib/Controller/PushController.php
+++ b/lib/Controller/PushController.php
@@ -117,7 +117,20 @@ class PushController extends OCSController {
openssl_sign($deviceIdentifier, $signature, $key->getPrivate(), OPENSSL_ALGO_SHA512);
$deviceIdentifier = base64_encode(hash('sha512', $deviceIdentifier, true));
- $created = $this->savePushToken($user, $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer);
+ $appType = 'unknown';
+ if ($this->request->isUserAgent([
+ IRequest::USER_AGENT_TALK_ANDROID,
+ IRequest::USER_AGENT_TALK_IOS,
+ ])) {
+ $appType = 'talk';
+ } else if ($this->request->isUserAgent([
+ IRequest::USER_AGENT_CLIENT_ANDROID,
+ IRequest::USER_AGENT_CLIENT_IOS,
+ ])) {
+ $appType = 'nextcloud';
+ }
+
+ $created = $this->savePushToken($user, $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer, $appType);
return new DataResponse([
'publicKey' => $key->getPublic(),
@@ -158,9 +171,10 @@ class PushController extends OCSController {
* @param string $devicePublicKey
* @param string $pushTokenHash
* @param string $proxyServer
+ * @param string $appType
* @return bool If the hash was new to the database
*/
- protected function savePushToken(IUser $user, IToken $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer) {
+ protected function savePushToken(IUser $user, IToken $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer, $appType) {
$query = $this->db->getQueryBuilder();
$query->select('*')
->from('notifications_pushtokens')
@@ -171,10 +185,10 @@ class PushController extends OCSController {
$result->closeCursor();
if (!$row) {
- return $this->insertPushToken($user, $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer);
+ return $this->insertPushToken($user, $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer, $appType);
}
- return $this->updatePushToken($user, $token, $devicePublicKey, $pushTokenHash, $proxyServer);
+ return $this->updatePushToken($user, $token, $devicePublicKey, $pushTokenHash, $proxyServer, $appType);
}
/**
@@ -184,9 +198,10 @@ class PushController extends OCSController {
* @param string $devicePublicKey
* @param string $pushTokenHash
* @param string $proxyServer
+ * @param string $appType
* @return bool If the entry was created
*/
- protected function insertPushToken(IUser $user, IToken $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer) {
+ protected function insertPushToken(IUser $user, IToken $token, $deviceIdentifier, $devicePublicKey, $pushTokenHash, $proxyServer, $appType) {
$devicePublicKeyHash = hash('sha512', $devicePublicKey);
$query = $this->db->getQueryBuilder();
@@ -199,6 +214,7 @@ class PushController extends OCSController {
'devicepublickeyhash' => $query->createNamedParameter($devicePublicKeyHash),
'pushtokenhash' => $query->createNamedParameter($pushTokenHash),
'proxyserver' => $query->createNamedParameter($proxyServer),
+ 'apptype' => $query->createNamedParameter($appType),
]);
return $query->execute() > 0;
}
@@ -209,9 +225,10 @@ class PushController extends OCSController {
* @param string $devicePublicKey
* @param string $pushTokenHash
* @param string $proxyServer
+ * @param string $appType
* @return bool If the entry was updated
*/
- protected function updatePushToken(IUser $user, IToken $token, $devicePublicKey, $pushTokenHash, $proxyServer) {
+ protected function updatePushToken(IUser $user, IToken $token, $devicePublicKey, $pushTokenHash, $proxyServer, $appType) {
$devicePublicKeyHash = hash('sha512', $devicePublicKey);
$query = $this->db->getQueryBuilder();
@@ -220,6 +237,7 @@ class PushController extends OCSController {
->set('devicepublickeyhash', $query->createNamedParameter($devicePublicKeyHash))
->set('pushtokenhash', $query->createNamedParameter($pushTokenHash))
->set('proxyserver', $query->createNamedParameter($proxyServer))
+ ->set('apptype', $query->createNamedParameter($appType))
->where($query->expr()->eq('uid', $query->createNamedParameter($user->getUID())))
->andWhere($query->expr()->eq('token', $query->createNamedParameter($token->getId(), IQueryBuilder::PARAM_INT)));
diff --git a/lib/Push.php b/lib/Push.php
index 30b7763..f9e1b2b 100644
--- a/lib/Push.php
+++ b/lib/Push.php
@@ -89,10 +89,32 @@ class Push {
$userKey = $this->keyManager->getKey($user);
+ $isTalkNotification = in_array($notification->getApp(), ['spreed', 'talk'], true)
+ && in_array($notification->getSubject(), ['invitation', 'call', 'mention'], true);
+ $talkApps = array_filter($devices, function($device) {
+ return $device['apptype'] === 'talk';
+ });
+ $hasTalkApps = !empty($talkApps);
+
$pushNotifications = [];
foreach ($devices as $device) {
+ if (!$isTalkNotification && $device['apptype'] === 'talk') {
+ // The iOS app can not kill notifications,
+ // therefor we should only send relevant notifications to the Talk
+ // app, so it does not pollute the notifications bar with useless
+ // notifications, especially when the Sync client app is also installed.
+ continue;
+ }
+ if ($isTalkNotification && $hasTalkApps && $device['apptype'] !== 'talk') {
+ // Similar to the previous case, we also don't send Talk notifications
+ // to the Sync client app, when there is a Talk app installed. We only
+ // do this, when you don't have a Talk app on your device, so you still
+ // get the push notification.
+ continue;
+ }
+
try {
- $payload = json_encode($this->encryptAndSign($userKey, $device, $notification));
+ $payload = json_encode($this->encryptAndSign($userKey, $device, $notification, $isTalkNotification));
$proxyServer = rtrim($device['proxyserver'], '/');
if (!isset($pushNotifications[$proxyServer])) {
@@ -150,11 +172,12 @@ class Push {
* @param Key $userKey
* @param array $device
* @param INotification $notification
+ * @param bool $isTalkNotification
* @return array
* @throws InvalidTokenException
* @throws \InvalidArgumentException
*/
- protected function encryptAndSign(Key $userKey, array $device, INotification $notification) {
+ protected function encryptAndSign(Key $userKey, array $device, INotification $notification, $isTalkNotification) {
// Check if the token is still valid...
$this->tokenProvider->getTokenById($device['token']);
@@ -163,6 +186,11 @@ class Push {
'subject' => $notification->getParsedSubject(),
];
+ if ($isTalkNotification) {
+ $data['type'] = $notification->getObjectType();
+ $data['id'] = $notification->getObjectId();
+ }
+
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');
diff --git a/tests/Unit/PushTest.php b/tests/Unit/PushTest.php
index 6399b37..9b0b7e0 100644
--- a/tests/Unit/PushTest.php
+++ b/tests/Unit/PushTest.php
@@ -224,6 +224,7 @@ class PushTest extends TestCase {
->willReturn([[
'proxyserver' => 'proxyserver1',
'token' => 23,
+ 'apptype' => 'other',
]]);
$this->config->expects($this->once())
@@ -280,6 +281,7 @@ class PushTest extends TestCase {
->willReturn([[
'proxyserver' => 'proxyserver1',
'token' => 23,
+ 'apptype' => 'other',
]]);
$this->config->expects($this->once())
@@ -346,22 +348,27 @@ class PushTest extends TestCase {
[
'proxyserver' => 'proxyserver1',
'token' => 16,
+ 'apptype' => 'other',
],
[
'proxyserver' => 'proxyserver1/',
'token' => 23,
+ 'apptype' => 'other',
],
[
'proxyserver' => 'badrequest',
'token' => 42,
+ 'apptype' => 'other',
],
[
'proxyserver' => 'unavailable',
'token' => 48,
+ 'apptype' => 'other',
],
[
'proxyserver' => 'ok',
'token' => 64,
+ 'apptype' => 'other',
],
]);
@@ -484,4 +491,127 @@ class PushTest extends TestCase {
$push->pushToDevice($notification);
}
+
+ public function dataPushToDeviceTalkNotification() {
+ return [
+ [['nextcloud'], false, 0],
+ [['nextcloud'], true, 0],
+ [['nextcloud', 'talk'], false, 0],
+ [['nextcloud', 'talk'], true, 1],
+ [['talk', 'nextcloud'], false, 1],
+ [['talk', 'nextcloud'], true, 0],
+ [['talk'], false, null],
+ [['talk'], true, 0],
+ ];
+ }
+
+ /**
+ * @dataProvider dataPushToDeviceTalkNotification
+ * @param string[] $deviceTypes
+ * @param bool $isTalkNotification
+ * @param int $pushedDevice
+ */
+ public function testPushToDeviceTalkNotification(array $deviceTypes, $isTalkNotification, $pushedDevice) {
+ $push = $this->getPush(['getDevicesForUser', 'encryptAndSign', 'deletePushToken']);
+
+ /** @var INotification|\PHPUnit_Framework_MockObject_MockObject $notification */
+ $notification = $this->createMock(INotification::class);
+ $notification->expects($this->exactly(3))
+ ->method('getUser')
+ ->willReturn('valid');
+
+ if ($isTalkNotification) {
+ $notification->expects($this->once())
+ ->method('getApp')
+ ->willReturn('spreed');
+ $notification->expects($this->once())
+ ->method('getSubject')
+ ->willReturn('call');
+ } else {
+ $notification->expects($this->once())
+ ->method('getApp')
+ ->willReturn('notifications');
+ $notification->expects($this->never())
+ ->method('getSubject');
+ }
+
+ /** @var IUser|\PHPUnit_Framework_MockObject_MockObject $user */
+ $user = $this->createMock(IUser::class);
+
+ $this->userManager->expects($this->once())
+ ->method('get')
+ ->with('valid')
+ ->willReturn($user);
+
+ $devices = [];
+ foreach ($deviceTypes as $deviceType) {
+ $devices[] = [
+ 'proxyserver' => 'proxyserver',
+ 'token' => strlen($deviceType),
+ 'apptype' => $deviceType,
+ ];
+ }
+ $push->expects($this->once())
+ ->method('getDevicesForUser')
+ ->willReturn($devices);
+
+ $this->config->expects($this->once())
+ ->method('getUserValue')
+ ->with('valid', 'core', 'lang', 'en')
+ ->willReturn('ru');
+
+ $this->notificationManager->expects($this->once())
+ ->method('prepare')
+ ->with($notification, 'ru')
+ ->willReturnArgument(0);
+
+ /** @var Key|\PHPUnit_Framework_MockObject_MockObject $key */
+ $key = $this->createMock(Key::class);
+
+ $this->keyManager->expects($this->once())
+ ->method('getKey')
+ ->with($user)
+ ->willReturn($key);
+
+ if ($pushedDevice === null) {
+ $push->expects($this->never())
+ ->method('encryptAndSign');
+
+ $this->clientService->expects($this->never())
+ ->method('newClient');
+ } else {
+ $push->expects($this->exactly(1))
+ ->method('encryptAndSign')
+ ->with($this->anything(), $devices[$pushedDevice], $this->anything(), $isTalkNotification)
+ ->willReturn(['Payload']);
+
+ /** @var IClient|\PHPUnit_Framework_MockObject_MockObject $client */
+ $client = $this->createMock(IClient::class);
+
+ $this->clientService->expects($this->once())
+ ->method('newClient')
+ ->willReturn($client);
+
+ /** @var IResponse|\PHPUnit_Framework_MockObject_MockObject $response1 */
+ $response = $this->createMock(IResponse::class);
+ $response->expects($this->once())
+ ->method('getStatusCode')
+ ->willReturn(Http::STATUS_BAD_REQUEST);
+ $response->expects($this->once())
+ ->method('getBody')
+ ->willReturn(null);
+ $client->expects($this->once())
+ ->method('post')
+ ->with('proxyserver/notifications', [
+ 'body' => [
+ 'notifications' => ['["Payload"]'],
+ ],
+ ])
+ ->willReturn($response);
+ }
+
+ $push->pushToDevice($notification);
+ }
+
+
}