Merge pull request #601 from BeaudanBrown/sync-messages

[multi-device] Sync messages
pull/605/head
Beaudan Campbell-Brown 6 years ago committed by GitHub
commit 0f74a002df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -8,6 +8,7 @@
storage,
textsecure,
Whisper,
libloki,
BlockedNumberController
*/
@ -233,9 +234,8 @@
specialConvInited = true;
};
let initialisedAPI = false;
const initAPIs = async () => {
if (initialisedAPI) {
if (window.initialisedAPI) {
return;
}
const ourKey = textsecure.storage.user.getNumber();
@ -253,15 +253,16 @@
window.lokiP2pAPI = new window.LokiP2pAPI(ourKey);
window.lokiP2pAPI.on('pingContact', pubKey => {
const isPing = true;
window.libloki.api.sendOnlineBroadcastMessage(pubKey, isPing);
libloki.api.sendOnlineBroadcastMessage(pubKey, isPing);
});
window.lokiP2pAPI.on('online', ConversationController._handleOnline);
window.lokiP2pAPI.on('offline', ConversationController._handleOffline);
initialisedAPI = true;
window.initialisedAPI = true;
if (storage.get('isSecondaryDevice')) {
window.lokiFileServerAPI.updateOurDeviceMapping();
}
Whisper.events.trigger('apisReady');
};
function mapOldThemeToNew(theme) {
@ -851,8 +852,8 @@
});
Whisper.events.on('devicePairingRequestRejected', async pubKey => {
await window.libloki.storage.removeContactPreKeyBundle(pubKey);
await window.libloki.storage.removePairingAuthorisationForSecondaryPubKey(
await libloki.storage.removeContactPreKeyBundle(pubKey);
await libloki.storage.removePairingAuthorisationForSecondaryPubKey(
pubKey
);
});
@ -1170,7 +1171,7 @@
}
let primaryDevice = null;
const authorisation = await window.libloki.storage.getGrantAuthorisationForSecondaryPubKey(
const authorisation = await libloki.storage.getGrantAuthorisationForSecondaryPubKey(
sender
);
if (authorisation) {
@ -1227,6 +1228,16 @@
if (activeAt !== null) {
activeAt = activeAt || Date.now();
}
const ourAuthorisations = await libloki.storage.getPrimaryDeviceMapping(
window.storage.get('primaryDevicePubKey')
);
const isSecondaryDevice =
ourAuthorisations &&
ourAuthorisations.some(auth => auth.secondaryDevicePubKey === id);
if (isSecondaryDevice) {
await conversation.setSecondaryStatus(true);
}
if (details.profileKey) {
const profileKey = window.Signal.Crypto.arrayBufferToBase64(
@ -1401,7 +1412,7 @@
const messageDescriptor = getMessageDescriptor(data);
// Funnel messages to primary device conversation if multi-device
const authorisation = await window.libloki.storage.getGrantAuthorisationForSecondaryPubKey(
const authorisation = await libloki.storage.getGrantAuthorisationForSecondaryPubKey(
messageDescriptor.id
);
if (authorisation) {

@ -192,7 +192,7 @@
},
isMe() {
return this.id === this.ourNumber;
return this.id === window.storage.get('primaryDevicePubKey');
},
isPublic() {
return this.id && this.id.match(/^publicChat:/);

@ -1269,7 +1269,9 @@
});
this.trigger('sent', this);
this.sendSyncMessage();
if (this.get('type') !== 'friend-request') {
this.sendSyncMessage();
}
})
.catch(result => {
this.trigger('done');
@ -1710,6 +1712,7 @@
// 2. on a sent message sync'd from another device
// 3. in rare cases, an incoming message can be retried, though it will
// still go through one of the previous two codepaths
const ourNumber = textsecure.storage.user.getNumber();
const message = this;
const source = message.get('source');
const type = message.get('type');
@ -1719,7 +1722,8 @@
);
if (initialMessage.group) {
conversationId = initialMessage.group.id;
} else if (authorisation) {
} else if (source !== ourNumber && authorisation) {
// Ignore auth from our devices
conversationId = authorisation.primaryDevicePubKey;
}
@ -1916,8 +1920,6 @@
c.onReadMessage(message);
}
} else {
const ourNumber = textsecure.storage.user.getNumber();
if (
message.attributes.body &&
message.attributes.body.indexOf(`@${ourNumber}`) !== -1
@ -2007,12 +2009,12 @@
autoAccept = true;
message.set({ friendStatus: 'accepted' });
await sendingDeviceConversation.onFriendRequestAccepted();
window.libloki.api.sendBackgroundMessage(message.get('source'));
} else {
await sendingDeviceConversation.onFriendRequestReceived();
}
} else {
await conversation.onFriendRequestAccepted();
} else if (message.get('type') !== 'outgoing') {
// Ignore 'outgoing' messages because they are sync messages
await sendingDeviceConversation.onFriendRequestAccepted();
// We need to return for these types of messages because android struggles
if (
!message.get('body') &&

@ -164,13 +164,21 @@
const pairingAuthorisation = createPairingAuthorisationProtoMessage(
authorisation
);
// Send profile name to secondary device
const ourNumber = textsecure.storage.user.getNumber();
const conversation = await ConversationController.getOrCreateAndWait(
const ourConversation = await ConversationController.getOrCreateAndWait(
ourNumber,
'private'
);
const lokiProfile = conversation.getLokiProfile();
const secondaryConversation = await ConversationController.getOrCreateAndWait(
recipientPubKey,
'private'
);
// Always be friends with secondary devices
secondaryConversation.setFriendRequestStatus(
window.friends.friendRequestStatusEnum.friends
);
// Send profile name to secondary device
const lokiProfile = ourConversation.getLokiProfile();
const profile = new textsecure.protobuf.DataMessage.LokiProfile(
lokiProfile
);
@ -178,12 +186,11 @@
profile,
});
// Attach contact list
// TODO: Reenable sending of the syncmessage for pairing requests
// const syncMessage = await createContactSyncProtoMessage();
const syncMessage = await createContactSyncProtoMessage();
const content = new textsecure.protobuf.Content({
pairingAuthorisation,
dataMessage,
// syncMessage,
syncMessage,
});
// Send
const options = { messageType: 'pairing-request' };

@ -239,6 +239,7 @@
getAuthorisationForSecondaryPubKey,
getAllDevicePubKeysForPrimaryPubKey,
getSecondaryDevicesFor,
getPrimaryDeviceMapping,
};
// Libloki protocol store

@ -546,6 +546,10 @@
async registrationDone(number, displayName) {
window.log.info('registration done');
if (!textsecure.storage.get('secondaryDeviceStatus')) {
// We have registered as a primary device
textsecure.storage.put('primaryDevicePubKey', number);
}
// Ensure that we always have a conversation for ourself
const conversation = await ConversationController.getOrCreateAndWait(
number,

@ -21,6 +21,7 @@
/* global Whisper: false */
/* global lokiFileServerAPI: false */
/* global WebAPI: false */
/* global ConversationController: false */
/* eslint-disable more/no-then */
/* eslint-disable no-unreachable */
@ -1018,7 +1019,9 @@ MessageReceiver.prototype.extend({
this.processDecrypted(envelope, msg).then(message => {
const groupId = message.group && message.group.id;
const isBlocked = this.isGroupBlocked(groupId);
const isMe = envelope.source === textsecure.storage.user.getNumber();
const isMe =
envelope.source === textsecure.storage.user.getNumber() ||
envelope.source === window.storage.get('primaryDevicePubKey');
const isLeavingGroup = Boolean(
message.group &&
message.group.type === textsecure.protobuf.GroupContext.Type.QUIT
@ -1108,6 +1111,11 @@ MessageReceiver.prototype.extend({
window.storage.remove('secondaryDeviceStatus');
window.storage.put('isSecondaryDevice', true);
window.storage.put('primaryDevicePubKey', primaryDevicePubKey);
const primaryConversation = await ConversationController.getOrCreateAndWait(
primaryDevicePubKey,
'private'
);
primaryConversation.trigger('change');
Whisper.events.trigger('secondaryDeviceRegistration');
// Update profile name
if (dataMessage && dataMessage.profile) {
@ -1122,7 +1130,14 @@ MessageReceiver.prototype.extend({
// This call already removes the envelope from the cache
await this.handleContacts(envelope, syncMessage.contacts);
removedFromCache = true;
await this.sendFriendRequestsToSyncContacts(syncMessage.contacts);
if (window.initialisedAPI) {
await this.sendFriendRequestsToSyncContacts(syncMessage.contacts);
} else {
// We need to wait here because initAPIs hasn't been called yet
Whisper.events.once('apisReady', async () => {
await this.sendFriendRequestsToSyncContacts(syncMessage.contacts);
});
}
}
} else {
window.log.warn('Unimplemented pairing authorisation message type');
@ -1451,13 +1466,23 @@ MessageReceiver.prototype.extend({
window.log.info('null message from', this.getEnvelopeId(envelope));
this.removeFromCache(envelope);
},
handleSyncMessage(envelope, syncMessage) {
if (envelope.source !== this.number) {
throw new Error('Received sync message from another number');
}
// eslint-disable-next-line eqeqeq
if (envelope.sourceDevice == this.deviceId) {
throw new Error('Received sync message from our own device');
async handleSyncMessage(envelope, syncMessage) {
const ourNumber = textsecure.storage.user.getNumber();
// NOTE: Maybe we should be caching this list?
const ourAuthorisations = await libloki.storage.getPrimaryDeviceMapping(
ourNumber
);
const validSyncSender =
ourAuthorisations &&
ourAuthorisations.some(
auth =>
auth.secondaryDevicePubKey === ourNumber ||
auth.primaryDevicePubKey === ourNumber
);
if (!validSyncSender) {
throw new Error(
"Received sync message from a device we aren't paired with"
);
}
if (syncMessage.sent) {
const sentMessage = syncMessage.sent;

@ -95,18 +95,18 @@ OutgoingMessage.prototype = {
this.numberCompleted();
},
reloadDevicesAndSend(number, recurse) {
const ourNumber = textsecure.storage.user.getNumber();
return () =>
libloki.storage
.getAllDevicePubKeysForPrimaryPubKey(number)
// Don't send to ourselves
.then(devicesPubKeys =>
devicesPubKeys.filter(pubKey => pubKey !== ourNumber)
)
.then(devicesPubKeys => {
if (devicesPubKeys.length === 0) {
// eslint-disable-next-line no-param-reassign
devicesPubKeys = [number];
// return this.registerError(
// number,
// 'Got empty device list when loading device keys',
// null
// );
}
return this.doSendMessage(number, devicesPubKeys, recurse);
});

@ -1,4 +1,4 @@
/* global _, textsecure, WebAPI, libsignal, OutgoingMessage, window */
/* global _, textsecure, WebAPI, libsignal, OutgoingMessage, window, libloki */
/* eslint-disable more/no-then, no-bitwise */
@ -434,7 +434,7 @@ MessageSender.prototype = {
return syncMessage;
},
sendSyncMessage(
async sendSyncMessage(
encodedDataMessage,
timestamp,
destination,
@ -443,9 +443,15 @@ MessageSender.prototype = {
unidentifiedDeliveries = [],
options
) {
const myNumber = textsecure.storage.user.getNumber();
const myDevice = textsecure.storage.user.getDeviceId();
if (myDevice === 1 || myDevice === '1') {
const primaryDeviceKey =
window.storage.get('primaryDevicePubKey') ||
textsecure.storage.user.getNumber();
const allOurDevices = (await libloki.storage.getAllDevicePubKeysForPrimaryPubKey(
primaryDeviceKey
))
// Don't send to ourselves
.filter(pubKey => pubKey !== textsecure.storage.user.getNumber());
if (allOurDevices.length === 0) {
return Promise.resolve();
}
@ -489,7 +495,7 @@ MessageSender.prototype = {
const silent = true;
return this.sendIndividualProto(
myNumber,
primaryDeviceKey,
contentMessage,
Date.now(),
silent,
@ -588,7 +594,8 @@ MessageSender.prototype = {
// We don't want to send typing messages to our other devices, but we will
// in the group case.
const myNumber = textsecure.storage.user.getNumber();
if (recipientId && myNumber === recipientId) {
const primaryDevicePubkey = window.storage.get('primaryDevicePubKey');
if (recipientId && primaryDevicePubkey === recipientId) {
return null;
}

@ -42,6 +42,7 @@ window.JobQueue = JobQueue;
window.getStoragePubKey = key =>
window.isDev() ? key.substring(0, key.length - 2) : key;
window.getDefaultFileServer = () => config.defaultFileServer;
window.initialisedAPI = false;
window.isBeforeVersion = (toCheck, baseVersion) => {
try {

Loading…
Cancel
Save