Merge pull request #157 from BeaudanBrown/loki-address-broadcast

Loki address broadcast
pull/160/head
sachaaaaa 6 years ago committed by GitHub
commit 9c98a1f654
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -102,6 +102,7 @@ module.exports = grunt => {
libloki: {
src: [
'libloki/api.js',
'libloki/friends.js',
'libloki/crypto.js',
'libloki/service_nodes.js',
'libloki/storage.js',

@ -90,6 +90,7 @@ module.exports = {
updateConversation,
removeConversation,
getAllConversations,
getPubKeysWithFriendStatus,
getAllConversationIds,
getAllPrivateConversations,
getAllGroupsInvolvingId,
@ -1127,7 +1128,7 @@ async function getSwarmNodesByPubkey(pubkey) {
});
if (!row) {
return null;
return [];
}
return jsonToObject(row.json).swarmNodes;
@ -1280,6 +1281,18 @@ async function getAllConversations() {
return map(rows, row => jsonToObject(row.json));
}
async function getPubKeysWithFriendStatus(status) {
const rows = await db.all(
`SELECT id FROM conversations WHERE
friendRequestStatus = $status
ORDER BY id ASC;`,
{
$status: status,
}
);
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);

@ -1,6 +1,7 @@
{
"serverUrl": "random.snode",
"cdnUrl": "random.snode",
"localServerPort": "8081",
"messageServerPort": "8080",
"swarmServerPort": "8079",
"disableAutoUpdate": false,

@ -1,5 +1,6 @@
{
"storageProfile": "development1",
"localServerPort": "8082",
"disableAutoUpdate": true,
"openDevTools": true
}

@ -182,11 +182,10 @@
}
try {
const swarmNodes = await window.LokiSnodeAPI.getFreshSwarmNodes(id);
conversation.set({ swarmNodes});
await window.Signal.Data.saveConversation(conversation.attributes, {
Conversation: Whisper.Conversation,
});
window.LokiSnodeAPI.refreshSwarmNodesForPubKey(id);
} catch (error) {
window.log.error(
'Conversation save failed! ',

@ -41,18 +41,7 @@
} = window.Signal.Migrations;
// Possible conversation friend states
const FriendRequestStatusEnum = Object.freeze({
// New conversation, no messages sent or received
none: 0,
// This state is used to lock the input early while sending
pendingSend: 1,
// Friend request sent, awaiting response
requestSent: 2,
// Friend request received, awaiting user input
requestReceived: 3,
// We did it!
friends: 4,
});
const FriendRequestStatusEnum = window.friends.friendRequestStatusEnum;
// Possible session reset states
const SessionResetEnum = Object.freeze({

@ -110,6 +110,7 @@ module.exports = {
removeAllSessions,
getSwarmNodesByPubkey,
saveSwarmNodesForPubKey,
getConversationCount,
saveConversation,
@ -120,6 +121,7 @@ module.exports = {
_removeConversations,
getAllConversations,
getPubKeysWithFriendStatus,
getAllConversationIds,
getAllPrivateConversations,
getAllGroupsInvolvingId,
@ -673,6 +675,14 @@ async function getSwarmNodesByPubkey(pubkey) {
return channels.getSwarmNodesByPubkey(pubkey);
}
async function saveSwarmNodesForPubKey(pubKey, swarmNodes, { Conversation }) {
const conversation = await getConversationById(pubKey, { Conversation });
conversation.set({ swarmNodes });
await updateConversation(conversation.id, conversation.attributes, {
Conversation,
});
}
async function getConversationCount() {
return channels.getConversationCount();
}
@ -721,6 +731,10 @@ async function _removeConversations(ids) {
await channels.removeConversation(ids);
}
async function getPubKeysWithFriendStatus(status) {
return channels.getPubKeysWithFriendStatus(status);
}
async function getAllConversations({ ConversationCollection }) {
const conversations = await channels.getAllConversations();

@ -1,6 +1,6 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable no-loop-func */
/* global log, dcodeIO, window, callWorker */
/* global log, dcodeIO, window, callWorker, Whisper */
const fetch = require('node-fetch');
const _ = require('lodash');
@ -105,20 +105,15 @@ class LokiMessageAPI {
throw HTTPError('sendMessage: error response', response.status, result);
};
let swarmNodes;
try {
swarmNodes = await window.LokiSnodeAPI.getSwarmNodesByPubkey(pubKey);
} catch (e) {
throw new window.textsecure.EmptySwarmError(pubKey, e);
}
let swarmNodes = await window.Signal.Data.getSwarmNodesByPubkey(pubKey);
while (successfulRequests < MINIMUM_SUCCESSFUL_REQUESTS) {
if (!canResolve) {
throw new window.textsecure.DNSResolutionError('Sending messages');
}
if (!swarmNodes || swarmNodes.length === 0) {
if (swarmNodes.length === 0) {
swarmNodes = await window.LokiSnodeAPI.getFreshSwarmNodes(pubKey);
swarmNodes = _.difference(swarmNodes, completedNodes);
if (!swarmNodes || swarmNodes.length === 0) {
if (swarmNodes.length === 0) {
if (successfulRequests !== 0) {
// TODO: Decide how to handle some completed requests but not enough
return;
@ -128,7 +123,9 @@ class LokiMessageAPI {
new Error('Ran out of swarm nodes to query')
);
}
await window.LokiSnodeAPI.saveSwarmNodes(pubKey, swarmNodes);
await window.Signal.Data.saveSwarmNodesForPubKey(pubKey, swarmNodes, {
Conversation: Whisper.Conversation,
});
}
const remainingRequests =
MINIMUM_SUCCESSFUL_REQUESTS - completedNodes.length;

@ -0,0 +1,24 @@
class LokiP2pAPI {
constructor() {
this.contactP2pDetails = {};
}
addContactP2pDetails(pubKey, address, port) {
this.contactP2pDetails[pubKey] = {
address,
port,
};
}
getContactP2pDetails(pubKey) {
return this.contactP2pDetails[pubKey] || null;
}
removeContactP2pDetails(pubKey) {
delete this.contactP2pDetails[pubKey];
}
}
module.exports = {
LokiP2pAPI,
};

@ -106,24 +106,11 @@ class LokiSnodeAPI {
return this.ourSwarmNodes;
}
async getSwarmNodesByPubkey(pubKey) {
const swarmNodes = await window.Signal.Data.getSwarmNodesByPubkey(pubKey);
if (swarmNodes) {
return swarmNodes;
}
return [];
}
async saveSwarmNodes(pubKey, swarmNodes) {
const conversation = window.ConversationController.get(pubKey);
conversation.set({ swarmNodes });
await window.Signal.Data.updateConversation(
conversation.id,
conversation.attributes,
{
async refreshSwarmNodesForPubKey(pubKey) {
const newNodes = await this.getFreshSwarmNodes(pubKey);
await window.Signal.Data.saveSwarmNodesForPubKey(pubKey, newNodes, {
Conversation: Whisper.Conversation,
}
);
});
}
async getFreshSwarmNodes(pubKey) {

@ -8,6 +8,53 @@
return sendEmptyMessage(pubKey, true);
}
async function broadcastOnlineStatus() {
const friendKeys = await window.Signal.Data.getPubKeysWithFriendStatus(
window.friends.friendRequestStatusEnum.friends
);
await Promise.all(
friendKeys.map(async pubKey => {
try {
await sendOnlineBroadcastMessage(pubKey);
} catch (e) {
log.warn(`Failed to send online broadcast message to ${pubKey}`);
}
})
);
}
async function sendOnlineBroadcastMessage(pubKey) {
// TODO: Make this actually get a loki address rather than junk string
const lokiAddressMessage = new textsecure.protobuf.LokiAddressMessage({
p2pAddress: 'testAddress',
p2pPort: parseInt(window.localServerPort, 10),
});
const content = new textsecure.protobuf.Content({
lokiAddressMessage,
});
// 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 +99,7 @@
window.libloki.api = {
sendFriendRequestAccepted,
sendEmptyMessage,
sendOnlineBroadcastMessage,
broadcastOnlineStatus,
};
})();

@ -0,0 +1,22 @@
/* global window */
// eslint-disable-next-line func-names
(function() {
// Possible conversation friend states
const friendRequestStatusEnum = Object.freeze({
// New conversation, no messages sent or received
none: 0,
// This state is used to lock the input early while sending
pendingSend: 1,
// Friend request sent, awaiting response
requestSent: 2,
// Friend request received, awaiting user input
requestReceived: 3,
// We did it!
friends: 4,
});
window.friends = {
friendRequestStatusEnum,
};
})();

@ -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());

@ -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,11 @@ MessageReceiver.prototype.extend({
}
});
this.localServer.removeAllListeners();
this.localServer.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);
// 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}`)
);
});
// TODO: Rework this socket stuff to work with online messaging
const useWebSocket = false;
@ -135,7 +131,10 @@ MessageReceiver.prototype.extend({
}
if (this.localServer) {
this.localServer.removeAllListeners();
this.localServer.removeListener(
'message',
this.httpPollingResource.handleMessage
);
this.localServer = null;
}
},
@ -899,6 +898,15 @@ MessageReceiver.prototype.extend({
})
);
},
async handleLokiAddressMessage(envelope, lokiAddressMessage) {
const { p2pAddress, p2pPort } = lokiAddressMessage;
window.LokiP2pAPI.addContactP2pDetails(
envelope.source,
p2pAddress,
p2pPort
);
return this.removeFromCache(envelope);
},
handleDataMessage(envelope, msg) {
window.log.info('data message from', this.getEnvelopeId(envelope));
let p = Promise.resolve();
@ -1013,6 +1021,11 @@ MessageReceiver.prototype.extend({
envelope.source,
content.preKeyBundleMessage
);
if (content.lokiAddressMessage)
return this.handleLokiAddressMessage(
envelope,
content.lokiAddressMessage
);
if (content.syncMessage)
return this.handleSyncMessage(envelope, content.syncMessage);
if (content.dataMessage)

@ -336,8 +336,19 @@ OutgoingMessage.prototype = {
dcodeIO.ByteBuffer.wrap(ciphertext.body, 'binary').toArrayBuffer()
);
}
let ttl;
if (this.messageType === 'friend-request') {
ttl = 4 * 24 * 60 * 60; // 4 days for friend request message
} else if (this.messageType === 'onlineBroadcast') {
ttl = 10 * 60; // 10 minutes for online broadcast message
} else {
const hours = window.getMessageTTL() || 24; // 1 day default for any other message
ttl = hours * 60 * 60;
}
return {
type: ciphertext.type, // FallBackSessionCipher sets this to FRIEND_REQUEST
ttl,
ourKey,
sourceDevice: 1,
destinationRegistrationId: ciphertext.registrationId,
@ -349,17 +360,12 @@ OutgoingMessage.prototype = {
// TODO: handle multiple devices/messages per transmit
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;
}
await this.transmitMessage(number, socketMessage, this.timestamp, ttl);
await this.transmitMessage(
number,
socketMessage,
this.timestamp,
outgoingObject.ttl
);
this.successfulNumbers[this.successfulNumbers.length] = number;
this.numberCompleted();
})

@ -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,

@ -276,6 +276,10 @@ window.LokiSnodeAPI = new LokiSnodeAPI({
swarmServerPort: config.swarmServerPort,
});
const { LokiP2pAPI } = require('./js/modules/loki_p2p_api');
window.LokiP2pAPI = new LokiP2pAPI();
const { LokiMessageAPI } = require('./js/modules/loki_message_api');
window.LokiMessageAPI = new LokiMessageAPI({
@ -285,7 +289,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');

@ -35,6 +35,12 @@ message Content {
optional ReceiptMessage receiptMessage = 5;
optional TypingMessage typingMessage = 6;
optional PreKeyBundleMessage preKeyBundleMessage = 101;
optional LokiAddressMessage lokiAddressMessage = 102;
}
message LokiAddressMessage {
optional string p2pAddress = 1;
optional uint32 p2pPort = 2;
}
message PreKeyBundleMessage {

@ -360,6 +360,7 @@
<script type="text/javascript" src="../js/storage.js" data-cover></script>
<script type="text/javascript" src="../js/signal_protocol_store.js" data-cover></script>
<script type="text/javascript" src="../js/libtextsecure.js" data-cover></script>
<script type="text/javascript" src="../js/libloki.js" data-cover></script>
<script type="text/javascript" src="../js/libphonenumber-util.js"></script>
<script type='text/javascript' src='../js/models/profile.js' data-cover></script>

Loading…
Cancel
Save