From b417edfc9b048794d008a0765e212325de5233c8 Mon Sep 17 00:00:00 2001 From: Beaudan Date: Wed, 23 Jan 2019 16:44:03 +1100 Subject: [PATCH] Added new protos for online broadcast messages, added db hits for getting friends, local server now instantiated on the window, now sending online broadcast messages when the local server starts --- app/sql.js | 9 +++++++ config/default.json | 1 + js/modules/data.js | 9 +++++++ libloki/api.js | 41 +++++++++++++++++++++++++++++++ libloki/local_loki_server.js | 1 + libtextsecure/message_receiver.js | 30 ++++++++++++++-------- libtextsecure/outgoing_message.js | 27 +++++++++++++------- main.js | 1 + preload.js | 3 ++- protos/SignalService.proto | 7 ++++++ 10 files changed, 109 insertions(+), 20 deletions(-) diff --git a/app/sql.js b/app/sql.js index 7be7ec038..7ace662fb 100644 --- a/app/sql.js +++ b/app/sql.js @@ -90,6 +90,7 @@ module.exports = { updateConversation, removeConversation, getAllConversations, + getAllFriendIds, getAllConversationIds, getAllPrivateConversations, getAllGroupsInvolvingId, @@ -1280,6 +1281,14 @@ async function getAllConversations() { return map(rows, row => jsonToObject(row.json)); } +async function getAllFriendIds() { + // TODO: Maybe don't have this hardcoded to 4 (friends status in the enum) + const rows = await db.all( + 'SELECT id FROM conversations WHERE friendRequestStatus = 4 ORDER BY id ASC;' + ); + return map(rows, row => row.id); +} + async function getAllConversationIds() { const rows = await db.all('SELECT id FROM conversations ORDER BY id ASC;'); return map(rows, row => row.id); diff --git a/config/default.json b/config/default.json index 3b605aa6d..09b15a766 100644 --- a/config/default.json +++ b/config/default.json @@ -1,6 +1,7 @@ { "serverUrl": "random.snode", "cdnUrl": "random.snode", + "localServerPort": "8081", "messageServerPort": "8080", "swarmServerPort": "8079", "disableAutoUpdate": false, diff --git a/js/modules/data.js b/js/modules/data.js index e8afe5b14..91547b1f0 100644 --- a/js/modules/data.js +++ b/js/modules/data.js @@ -120,6 +120,7 @@ module.exports = { _removeConversations, getAllConversations, + getAllFriendIds, getAllConversationIds, getAllPrivateConversations, getAllGroupsInvolvingId, @@ -721,6 +722,14 @@ async function _removeConversations(ids) { await channels.removeConversation(ids); } +async function getAllFriendIds() { + const ids = (await channels.getAllFriendIds()).map(c => + setifyProperty(c, 'swarmNodes') + ); + + return ids; +} + async function getAllConversations({ ConversationCollection }) { const conversations = await channels.getAllConversations(); diff --git a/libloki/api.js b/libloki/api.js index 6c5c8365f..60682bf34 100644 --- a/libloki/api.js +++ b/libloki/api.js @@ -8,6 +8,45 @@ return sendEmptyMessage(pubKey, true); } + async function broadcastOnlineStatus() { + const friendKeys = await window.Signal.Data.getAllFriendIds(); + friendKeys.forEach(pubKey => { + sendOnlineBroadcastMessage(pubKey) + }); + } + + async function sendOnlineBroadcastMessage(pubKey) { + const onlineBroadcastMessage = new textsecure.protobuf.OnlineBroadcastMessage({ + snappAddress: 'testAddress', + port: parseInt(window.localServerPort, 10), + timestamp: Date.now(), + }); + const content = new textsecure.protobuf.Content({ + onlineBroadcastMessage, + }); + + // will be called once the transmission succeeded or failed + const callback = res => { + if (res.errors.length > 0) { + res.errors.forEach(error => log.error(error)); + } else { + log.info('Online broadcast message sent successfully'); + } + }; + const options = { messageType: 'onlineBroadcast' }; + // Send a empty message with information about how to contact us directly + const outgoingMessage = new textsecure.OutgoingMessage( + null, // server + Date.now(), // timestamp, + [pubKey], // numbers + content, // message + true, // silent + callback, // callback + options + ); + await outgoingMessage.sendToNumber(pubKey); + } + async function sendEmptyMessage(pubKey, sendContentMessage = false) { const options = {}; // send an empty message. @@ -52,5 +91,7 @@ window.libloki.api = { sendFriendRequestAccepted, sendEmptyMessage, + sendOnlineBroadcastMessage, + broadcastOnlineStatus, }; })(); diff --git a/libloki/local_loki_server.js b/libloki/local_loki_server.js index 3bfe40887..ed9bf6c03 100644 --- a/libloki/local_loki_server.js +++ b/libloki/local_loki_server.js @@ -68,6 +68,7 @@ class LocalLokiServer extends EventEmitter { // Async wrapper for http server close close() { + this.removeAllListeners(); if (this.server) { return new Promise(res => { this.server.close(() => res()); diff --git a/libtextsecure/message_receiver.js b/libtextsecure/message_receiver.js index e8a2dcb71..a076a52cf 100644 --- a/libtextsecure/message_receiver.js +++ b/libtextsecure/message_receiver.js @@ -23,7 +23,7 @@ function MessageReceiver(username, password, signalingKey, options = {}) { this.username = username; this.password = password; this.lokiMessageAPI = window.LokiMessageAPI; - this.localServer = new window.LocalLokiServer(); + this.localServer = window.LocalLokiServer; if (!options.serverTrustRoot) { throw new Error('Server trust root is required!'); @@ -82,15 +82,13 @@ MessageReceiver.prototype.extend({ } }); - this.localServer.removeAllListeners(); - this.localServer.on('message', this.httpPollingResource.handleMessage); - - // Passing 0 as the port will automatically assign an unused port this.localServer - .start(0) - .then(port => - window.log.info(`Local Server started at localhost:${port}`) - ); + .start(window.localServerPort) + .then(port => { + window.log.info(`Local Server started at localhost:${port}`); + window.libloki.api.broadcastOnlineStatus(); + this.localServer.on('message', this.httpPollingResource.handleMessage); + }); // TODO: Rework this socket stuff to work with online messaging const useWebSocket = false; @@ -135,7 +133,7 @@ MessageReceiver.prototype.extend({ } if (this.localServer) { - this.localServer.removeAllListeners(); + this.localServer.removeListener('message', this.httpPollingResource.handleMessage); this.localServer = null; } }, @@ -713,6 +711,13 @@ MessageReceiver.prototype.extend({ .then(this.unpad) .then(handleSessionReset); break; + case textsecure.protobuf.Envelope.Type.ONLINE_BROADCAST: + window.log.info('Online broadcast message from', this.getEnvelopeId(envelope)); + promise = captureActiveSession() + .then(() => sessionCipher.decryptWhisperMessage(ciphertext)) + .then(this.unpad) + .then(handleSessionReset); + break; case textsecure.protobuf.Envelope.Type.FRIEND_REQUEST: { window.log.info('friend-request message from ', envelope.source); promise = fallBackSessionCipher @@ -899,6 +904,9 @@ MessageReceiver.prototype.extend({ }) ); }, + handleOnlineBroadcastMessage(envelope, onlineBroadcastMessage) { + return this.removeFromCache(envelope); + }, handleDataMessage(envelope, msg) { window.log.info('data message from', this.getEnvelopeId(envelope)); let p = Promise.resolve(); @@ -1013,6 +1021,8 @@ MessageReceiver.prototype.extend({ envelope.source, content.preKeyBundleMessage ); + if (content.onlineBroadcastMessage) + return this.handleOnlineBroadcastMessage(envelope, content.onlineBroadcastMessage); if (content.syncMessage) return this.handleSyncMessage(envelope, content.syncMessage); if (content.dataMessage) diff --git a/libtextsecure/outgoing_message.js b/libtextsecure/outgoing_message.js index 668e4067e..066429844 100644 --- a/libtextsecure/outgoing_message.js +++ b/libtextsecure/outgoing_message.js @@ -336,8 +336,13 @@ OutgoingMessage.prototype = { dcodeIO.ByteBuffer.wrap(ciphertext.body, 'binary').toArrayBuffer() ); } + const outgoingObjectType = + this.messageType === 'onlineBroadcast' + ? textsecure.protobuf.Envelope.Type.ONLINE_BROADCAST + : ciphertext.type; // FallBackSessionCipher sets this to FRIEND_REQUEST + return { - type: ciphertext.type, // FallBackSessionCipher sets this to FRIEND_REQUEST + type: outgoingObjectType, ourKey, sourceDevice: 1, destinationRegistrationId: ciphertext.registrationId, @@ -350,14 +355,18 @@ OutgoingMessage.prototype = { const outgoingObject = outgoingObjects[0]; const socketMessage = await this.wrapInWebsocketMessage(outgoingObject); let ttl; - if ( - outgoingObject.type === - textsecure.protobuf.Envelope.Type.FRIEND_REQUEST - ) { - ttl = 4 * 24 * 60 * 60; // 4 days for friend request message - } else { - const hours = window.getMessageTTL() || 24; // 1 day default for any other message - ttl = hours * 60 * 60; + switch (outgoingObject.type) { + case textsecure.protobuf.Envelope.Type.FRIEND_REQUEST: + ttl = 4 * 24 * 60 * 60; // 4 days for friend request message + break; + case textsecure.protobuf.Envelope.Type.ONLINE_BROADCAST: + ttl = 10 * 60; // 10 minutes for online broadcast message + break; + default: { + const hours = window.getMessageTTL() || 24; // 1 day default for any other message + ttl = hours * 60 * 60; + break; + } } await this.transmitMessage(number, socketMessage, this.timestamp, ttl); this.successfulNumbers[this.successfulNumbers.length] = number; diff --git a/main.js b/main.js index 751904140..42445e529 100644 --- a/main.js +++ b/main.js @@ -146,6 +146,7 @@ function prepareURL(pathSegments, moreKeys) { cdnUrl: config.get('cdnUrl'), messageServerPort: config.get('messageServerPort'), swarmServerPort: config.get('swarmServerPort'), + localServerPort: config.get('localServerPort'), certificateAuthority: config.get('certificateAuthority'), environment: config.environment, node_version: process.versions.node, diff --git a/preload.js b/preload.js index 1a7b85b07..78e221457 100644 --- a/preload.js +++ b/preload.js @@ -285,7 +285,8 @@ window.LokiMessageAPI = new LokiMessageAPI({ const { LocalLokiServer } = require('./libloki/local_loki_server'); -window.LocalLokiServer = LocalLokiServer; +window.localServerPort = config.localServerPort; +window.LocalLokiServer = new LocalLokiServer(); window.mnemonic = require('./libloki/mnemonic'); const { WorkerInterface } = require('./js/modules/util_worker_interface'); diff --git a/protos/SignalService.proto b/protos/SignalService.proto index 61d6fa91d..aef66ec6d 100644 --- a/protos/SignalService.proto +++ b/protos/SignalService.proto @@ -13,6 +13,7 @@ message Envelope { RECEIPT = 5; UNIDENTIFIED_SENDER = 6; FRIEND_REQUEST = 101; // contains prekeys + message and is using simple encryption + ONLINE_BROADCAST = 102; // Contains address and port information for p2p messaging } optional Type type = 1; @@ -35,6 +36,12 @@ message Content { optional ReceiptMessage receiptMessage = 5; optional TypingMessage typingMessage = 6; optional PreKeyBundleMessage preKeyBundleMessage = 101; + optional OnlineBroadcastMessage onlineBroadcastMessage = 102; +} + +message OnlineBroadcastMessage { + optional string snappAddress = 1; + optional uint32 port = 2; } message PreKeyBundleMessage {