From 3a876c7322c97bde8df9aaf74e3a3e067c781d8a Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Mon, 21 Sep 2020 11:45:10 +1000 Subject: [PATCH] add notify to PN server on message sent --- js/models/messages.js | 61 +++++++++++++++---- js/modules/loki_app_dot_net_api.js | 2 + .../loki_push_notification_server_api.js | 28 +++++++++ js/views/inbox_view.js | 4 +- preload.js | 2 +- ts/session/sending/MessageQueue.ts | 4 +- ts/session/sending/MessageQueueInterface.ts | 5 +- ts/session/sending/MessageSender.ts | 8 ++- ts/session/snode_api/onions.ts | 9 ++- ts/session/snode_api/serviceNodeAPI.ts | 1 - 10 files changed, 100 insertions(+), 24 deletions(-) create mode 100644 js/modules/loki_push_notification_server_api.js diff --git a/js/models/messages.js b/js/models/messages.js index 4da827afc..1345f895a 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -1232,7 +1232,7 @@ * This function is called by inbox_view.js when a message was successfully sent for one device. * So it might be called several times for the same message */ - async handleMessageSentSuccess(sentMessage) { + async handleMessageSentSuccess(sentMessage, wrappedEnvelope) { let sentTo = this.get('sent_to') || []; const isOurDevice = await window.libsession.Protocols.MultiDeviceProtocol.isOurDevice( @@ -1253,32 +1253,67 @@ // if we did not sync or trigger a sync message for this specific message already const shouldTriggerSyncMessage = !isOurDevice && - !isOpenGroupMessage && !isMediumGroupMessage && !this.get('synced') && !this.get('sentSync'); // A message is synced if we triggered a sync message (sentSync) // and the current message was sent to our device (so a sync message) - const shouldMarkMessageAsSynced = - isOurDevice && !isOpenGroupMessage && this.get('sentSync'); + const shouldMarkMessageAsSynced = isOurDevice && this.get('sentSync'); - // Handle the sync logic here - if (shouldTriggerSyncMessage) { + const isSessionOrClosedMessage = !isOpenGroupMessage; + + if (!isOpenGroupMessage) { const contentDecoded = textsecure.protobuf.Content.decode( sentMessage.plainTextBuffer ); const { dataMessage } = contentDecoded; - if (dataMessage) { - await this.sendSyncMessage(dataMessage); - } - } else if (shouldMarkMessageAsSynced) { - this.set({ synced: true }); - } - if (!isOpenGroupMessage) { const primaryPubKey = await libsession.Protocols.MultiDeviceProtocol.getPrimaryDevice( sentMessage.device ); + + /** + * We should hit the notify endpoint for push notification only if: + * • It's a one-to-one chat or a closed group + * • The message has either text or attachments + */ + const hasBodyOrAttachments = Boolean( + dataMessage && + (dataMessage.body || + (dataMessage.attachments && dataMessage.attachments.length)) + ); + const shouldNotifyPushServer = + hasBodyOrAttachments && + isSessionOrClosedMessage && + sentMessage.ttl === + window.libsession.Constants.TTL_DEFAULT.REGULAR_MESSAGE; + if (shouldNotifyPushServer) { + // notify the push notification server if needed + if (!wrappedEnvelope) { + window.log.warn( + 'Should send PN notify but no wrapped envelope set.' + ); + } else { + if (!window.LokiPushNotificationServer) { + window.LokiPushNotificationServer = new window.LokiPushNotificationServerApi(); + } + + window.LokiPushNotificationServer.notify( + wrappedEnvelope, + primaryPubKey.key + ); + } + } + + // Handle the sync logic here + if (shouldTriggerSyncMessage) { + if (dataMessage) { + await this.sendSyncMessage(dataMessage); + } + } else if (shouldMarkMessageAsSynced) { + this.set({ synced: true }); + } + sentTo = _.union(sentTo, [primaryPubKey.key]); } diff --git a/js/modules/loki_app_dot_net_api.js b/js/modules/loki_app_dot_net_api.js index 4bedce58b..4b3472e26 100644 --- a/js/modules/loki_app_dot_net_api.js +++ b/js/modules/loki_app_dot_net_api.js @@ -31,6 +31,8 @@ const urlPubkeyMap = { 'https://file-dev.lokinet.org': LOKIFOUNDATION_DEVFILESERVER_PUBKEY, 'https://file.getsession.org': LOKIFOUNDATION_FILESERVER_PUBKEY, 'https://file.lokinet.org': LOKIFOUNDATION_FILESERVER_PUBKEY, + 'https://staging.apns.getsession.org': + 'BWQqZYWRl0LlotTcUSRJZPvNi8qyt1YSQH3li4EHQNBJ', }; const HOMESERVER_USER_ANNOTATION_TYPE = 'network.loki.messenger.homeserver'; diff --git a/js/modules/loki_push_notification_server_api.js b/js/modules/loki_push_notification_server_api.js new file mode 100644 index 000000000..f429af81e --- /dev/null +++ b/js/modules/loki_push_notification_server_api.js @@ -0,0 +1,28 @@ +/* global dcodeIO */ + +const LokiAppDotNetAPI = require('./loki_app_dot_net_api'); + +class LokiPushNotificationServerApi { + constructor() { + this.ourKey = + '642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049'; + this.serverUrl = 'https://staging.apns.getsession.org'; + this._server = new LokiAppDotNetAPI(this.ourKey, this.serverUrl); + + // make sure pubKey & pubKeyHex are set in _server + this.pubKey = this._server.getPubKeyForUrl(); + } + + async notify(plainTextBuffer, sentTo) { + const options = { + method: 'post', + objBody: { + data: dcodeIO.ByteBuffer.wrap(plainTextBuffer).toString('base64'), + send_to: sentTo, + }, + }; + return this._server.serverRequest('notify', options); + } +} + +module.exports = LokiPushNotificationServerApi; diff --git a/js/views/inbox_view.js b/js/views/inbox_view.js index 04464009e..b50de1d99 100644 --- a/js/views/inbox_view.js +++ b/js/views/inbox_view.js @@ -307,14 +307,14 @@ return { msg }; }, - async handleMessageSentSuccess(sentMessage) { + async handleMessageSentSuccess(sentMessage, wrappedEnvelope) { const fetchedData = await this.fetchHandleMessageSentData(sentMessage); if (!fetchedData) { return; } const { msg } = fetchedData; - msg.handleMessageSentSuccess(sentMessage); + msg.handleMessageSentSuccess(sentMessage, wrappedEnvelope); }, async handleMessageSentFailure(sentMessage, error) { diff --git a/preload.js b/preload.js index c22b99888..ff5848175 100644 --- a/preload.js +++ b/preload.js @@ -350,6 +350,7 @@ if (process.env.USE_STUBBED_NETWORK) { window.LokiPublicChatAPI = require('./js/modules/loki_public_chat_api'); window.LokiFileServerAPI = require('./js/modules/loki_file_server_api'); +window.LokiPushNotificationServerApi = require('./js/modules/loki_push_notification_server_api'); window.mnemonic = require('./libloki/modules/mnemonic'); const WorkerInterface = require('./js/modules/util_worker_interface'); @@ -504,7 +505,6 @@ const { } = require('./ts/util/blockedNumberController'); window.BlockedNumberController = BlockedNumberController; - window.deleteAccount = async reason => { try { window.log.info('Deleting everything!'); diff --git a/ts/session/sending/MessageQueue.ts b/ts/session/sending/MessageQueue.ts index d880e91f0..b5e86e41d 100644 --- a/ts/session/sending/MessageQueue.ts +++ b/ts/session/sending/MessageQueue.ts @@ -152,8 +152,8 @@ export class MessageQueue implements MessageQueueInterface { // We put the event handling inside this job to avoid sending duplicate events const job = async () => { try { - await MessageSender.send(message); - this.events.emit('success', message); + const wrappedEnvelope = await MessageSender.send(message); + this.events.emit('success', message, wrappedEnvelope); } catch (e) { this.events.emit('fail', message, e); } finally { diff --git a/ts/session/sending/MessageQueueInterface.ts b/ts/session/sending/MessageQueueInterface.ts index 3fb6df28a..c30592741 100644 --- a/ts/session/sending/MessageQueueInterface.ts +++ b/ts/session/sending/MessageQueueInterface.ts @@ -11,7 +11,10 @@ import { PubKey } from '../types'; type GroupMessageType = OpenGroupMessage | ClosedGroupMessage; export interface MessageQueueInterfaceEvents { - success: (message: RawMessage | OpenGroupMessage) => void; + success: ( + message: RawMessage | OpenGroupMessage, + wrappedEnvelope?: Uint8Array + ) => void; fail: (message: RawMessage | OpenGroupMessage, error: Error) => void; } diff --git a/ts/session/sending/MessageSender.ts b/ts/session/sending/MessageSender.ts index 6d28cbcb9..6341274d7 100644 --- a/ts/session/sending/MessageSender.ts +++ b/ts/session/sending/MessageSender.ts @@ -27,7 +27,7 @@ export function canSendToSnode(): boolean { export async function send( message: RawMessage, attempts: number = 3 -): Promise { +): Promise { if (!canSendToSnode()) { throw new Error('lokiMessageAPI is not initialized.'); } @@ -48,8 +48,10 @@ export async function send( const data = wrapEnvelope(envelope); return pRetry( - async () => - window.lokiMessageAPI.sendMessage(device.key, data, timestamp, ttl), + async () => { + await window.lokiMessageAPI.sendMessage(device.key, data, timestamp, ttl); + return data; + }, { retries: Math.max(attempts - 1, 0), factor: 1, diff --git a/ts/session/snode_api/onions.ts b/ts/session/snode_api/onions.ts index 176f4c23b..a7987e3cc 100644 --- a/ts/session/snode_api/onions.ts +++ b/ts/session/snode_api/onions.ts @@ -170,7 +170,7 @@ const processOnionResponse = async ( return RequestError.OTHER; } - const ciphertext = await response.text(); + let ciphertext = await response.text(); if (!ciphertext) { log.warn( `(${reqIdx}) [path] lokiRpc::processOnionResponse - Target node return empty ciphertext` @@ -186,6 +186,13 @@ const processOnionResponse = async ( let plaintext; let ciphertextBuffer; + + try { + const jsonRes = JSON.parse(ciphertext); + ciphertext = jsonRes.result; + } catch (e) { + // just try to get a json object from what is inside (for PN requests), if it fails, continue () + } try { ciphertextBuffer = dcodeIO.ByteBuffer.wrap( ciphertext, diff --git a/ts/session/snode_api/serviceNodeAPI.ts b/ts/session/snode_api/serviceNodeAPI.ts index 0d1bf96af..6b96520f9 100644 --- a/ts/session/snode_api/serviceNodeAPI.ts +++ b/ts/session/snode_api/serviceNodeAPI.ts @@ -3,7 +3,6 @@ import https from 'https'; import fetch from 'node-fetch'; -import { PubKey } from '../types'; import { snodeRpc } from './lokiRpc'; import { sendOnionRequestLsrpcDest, SnodeResponse } from './onions';