"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "js/background.js" between
Signal-Desktop-1.35.2.tar.gz and Signal-Desktop-1.36.1.tar.gz

About: Signal-Desktop is a cross-platform encrypted messaging service (also available for mobile devices).

background.js  (Signal-Desktop-1.35.2):background.js  (Signal-Desktop-1.36.1)
skipping to change at line 17 skipping to change at line 17
getAccountManager, getAccountManager,
Signal, Signal,
storage, storage,
textsecure, textsecure,
WebAPI, WebAPI,
Whisper, Whisper,
*/ */
// eslint-disable-next-line func-names // eslint-disable-next-line func-names
(async function() { (async function() {
'use strict';
const eventHandlerQueue = new window.PQueue({ concurrency: 1 }); const eventHandlerQueue = new window.PQueue({ concurrency: 1 });
Whisper.deliveryReceiptQueue = new window.PQueue({ Whisper.deliveryReceiptQueue = new window.PQueue({
concurrency: 1, concurrency: 1,
}); });
Whisper.deliveryReceiptQueue.pause(); Whisper.deliveryReceiptQueue.pause();
Whisper.deliveryReceiptBatcher = window.Signal.Util.createBatcher({ Whisper.deliveryReceiptBatcher = window.Signal.Util.createBatcher({
wait: 500, wait: 500,
maxSize: 500, maxSize: 500,
processBatch: async items => { processBatch: async items => {
const byConversationId = _.groupBy(items, item => const byConversationId = _.groupBy(items, item =>
skipping to change at line 233 skipping to change at line 231
window.owsDesktopApp = {}; window.owsDesktopApp = {};
window.document.title = window.getTitle(); window.document.title = window.getTitle();
Whisper.KeyChangeListener.init(textsecure.storage.protocol); Whisper.KeyChangeListener.init(textsecure.storage.protocol);
textsecure.storage.protocol.on('removePreKey', () => { textsecure.storage.protocol.on('removePreKey', () => {
getAccountManager().refreshPreKeys(); getAccountManager().refreshPreKeys();
}); });
let messageReceiver; let messageReceiver;
let preMessageReceiverStatus;
window.getSocketStatus = () => { window.getSocketStatus = () => {
if (messageReceiver) { if (messageReceiver) {
return messageReceiver.getStatus(); return messageReceiver.getStatus();
} }
if (_.isNumber(preMessageReceiverStatus)) {
return preMessageReceiverStatus;
}
return -1; return -1;
}; };
Whisper.events = _.clone(Backbone.Events); Whisper.events = _.clone(Backbone.Events);
let accountManager; let accountManager;
window.getAccountManager = () => { window.getAccountManager = () => {
if (!accountManager) { if (!accountManager) {
const OLD_USERNAME = storage.get('number_id'); const OLD_USERNAME = storage.get('number_id');
const USERNAME = storage.get('uuid_id'); const USERNAME = storage.get('uuid_id');
const PASSWORD = storage.get('password'); const PASSWORD = storage.get('password');
accountManager = new textsecure.AccountManager( accountManager = new textsecure.AccountManager(
skipping to change at line 547 skipping to change at line 549
window.isBeforeVersion(lastVersion, 'v1.25.0') && window.isBeforeVersion(lastVersion, 'v1.25.0') &&
window.platform === 'darwin' && window.platform === 'darwin' &&
newThemeSetting === window.systemTheme newThemeSetting === window.systemTheme
) { ) {
window.Events.setThemeSetting('system'); window.Events.setThemeSetting('system');
} else { } else {
window.Events.setThemeSetting(newThemeSetting); window.Events.setThemeSetting(newThemeSetting);
} }
if ( if (
window.isBeforeVersion(lastVersion, 'v1.35.0-beta.11') && window.isBeforeVersion(lastVersion, 'v1.36.0-beta.1') &&
window.isAfterVersion(lastVersion, 'v1.35.0-beta.1') window.isAfterVersion(lastVersion, 'v1.35.0-beta.1')
) { ) {
await window.Signal.Util.eraseAllStorageServiceState(); await window.Signal.Services.eraseAllStorageServiceState();
} }
// This one should always be last - it could restart the app // This one should always be last - it could restart the app
if (window.isBeforeVersion(lastVersion, 'v1.15.0-beta.5')) { if (window.isBeforeVersion(lastVersion, 'v1.15.0-beta.5')) {
await window.Signal.Logs.deleteAll(); await window.Signal.Logs.deleteAll();
window.restart(); window.restart();
return; return;
} }
} }
skipping to change at line 607 skipping to change at line 609
idleDetector.stop(); idleDetector.stop();
} }
}); });
window.Signal.conversationControllerStart(); window.Signal.conversationControllerStart();
// We start this up before ConversationController.load() to ensure that our feature // We start this up before ConversationController.load() to ensure that our feature
// flags are represented in the cached props we generate on load of each c onvo. // flags are represented in the cached props we generate on load of each c onvo.
window.Signal.RemoteConfig.initRemoteConfig(); window.Signal.RemoteConfig.initRemoteConfig();
// On startup, we don't want to wait for the remote config fetch if we've al
ready
// learned that this instance supports GroupsV2.
// This is how we keep it sticky. Once it is enabled, we never disable it.
if (
window.Signal.RemoteConfig.isEnabled('desktop.gv2') ||
window.storage.get('gv2-enabled')
) {
window.GV2 = true;
window.storage.put('gv2-enabled', true);
}
try { try {
await Promise.all([ await Promise.all([
ConversationController.load(), ConversationController.load(),
Signal.Stickers.load(), Signal.Stickers.load(),
Signal.Emojis.load(), Signal.Emojis.load(),
textsecure.storage.protocol.hydrateCaches(), textsecure.storage.protocol.hydrateCaches(),
]); ]);
await ConversationController.checkForConflicts(); await ConversationController.checkForConflicts();
} catch (error) { } catch (error) {
window.log.error( window.log.error(
skipping to change at line 816 skipping to change at line 829
} }
for ( for (
let i = startIndex, max = toSearch.length; let i = startIndex, max = toSearch.length;
i >= 0 && i < max; i >= 0 && i < max;
i += increment i += increment
) { ) {
const target = toSearch[i]; const target = toSearch[i];
if (!unreadOnly) { if (!unreadOnly) {
return target.id; return target.id;
} else if (target.unreadCount > 0) { }
if (target.unreadCount > 0) {
return target.id; return target.id;
} }
} }
return null; return null;
} }
const NUMBERS = { const NUMBERS = {
'1': 1, '1': 1,
'2': 2, '2': 2,
skipping to change at line 1532 skipping to change at line 1546
initialLoadComplete, initialLoadComplete,
}); });
} }
}); });
// Maybe refresh remote configuration when we become active // Maybe refresh remote configuration when we become active
window.registerForActive(async () => { window.registerForActive(async () => {
await window.Signal.RemoteConfig.maybeRefreshRemoteConfig(); await window.Signal.RemoteConfig.maybeRefreshRemoteConfig();
}); });
// Listen for changes to the `desktop.clientExpiration` remote flag
window.Signal.RemoteConfig.onChange(
'desktop.clientExpiration',
({ value }) => {
const remoteBuildExpirationTimestamp = window.Signal.Util.parseRemoteCli
entExpiration(
value
);
if (remoteBuildExpirationTimestamp) {
window.storage.put(
'remoteBuildExpiration',
remoteBuildExpirationTimestamp
);
window.reduxActions.expiration.hydrateExpirationStatus(
window.Signal.Util.hasExpired()
);
}
}
);
// Listen for changes to the `desktop.messageRequests` remote configuration flag // Listen for changes to the `desktop.messageRequests` remote configuration flag
const removeMessageRequestListener = window.Signal.RemoteConfig.onChange( const removeMessageRequestListener = window.Signal.RemoteConfig.onChange(
'desktop.messageRequests', 'desktop.messageRequests',
({ enabled }) => { ({ enabled }) => {
if (!enabled) { if (!enabled) {
return; return;
} }
const conversations = window.getConversations(); const conversations = window.getConversations();
conversations.forEach(conversation => { conversations.forEach(conversation => {
conversation.set({ conversation.set({
messageCountBeforeMessageRequests: messageCountBeforeMessageRequests:
conversation.get('messageCount') || 0, conversation.get('messageCount') || 0,
}); });
window.Signal.Data.updateConversation(conversation.attributes); window.Signal.Data.updateConversation(conversation.attributes);
}); });
removeMessageRequestListener(); removeMessageRequestListener();
} }
); );
// Listen for changes to the `desktop.gv2` remote configuration flag
const removeGv2Listener = window.Signal.RemoteConfig.onChange(
'desktop.gv2',
async ({ enabled }) => {
if (!enabled) {
return;
}
window.GV2 = true;
await window.storage.put('gv2-enabled', true);
window.Signal.Services.handleUnknownRecords(
window.textsecure.protobuf.ManifestRecord.Identifier.Type.GROUPV2
);
// Erase current manifest version so we re-process storage service data
await window.storage.remove('manifestVersion');
// Kick off storage service fetch to grab GroupV2 information
await window.Signal.Services.runStorageServiceSyncJob();
// This is a one-time thing
removeGv2Listener();
}
);
window.Signal.RemoteConfig.onChange(
'desktop.storage',
async ({ enabled }) => {
if (!enabled) {
await window.storage.remove('storageKey');
return;
}
await window.storage.remove('manifestVersion');
await window.textsecure.messaging.sendRequestKeySyncMessage();
}
);
} }
window.getSyncRequest = () => window.getSyncRequest = () =>
new textsecure.SyncRequest(textsecure.messaging, messageReceiver); new textsecure.SyncRequest(textsecure.messaging, messageReceiver);
let disconnectTimer = null; let disconnectTimer = null;
let reconnectTimer = null; let reconnectTimer = null;
function onOffline() { function onOffline() {
window.log.info('offline'); window.log.info('offline');
skipping to change at line 1636 skipping to change at line 1709
); );
window.addEventListener('online', onOnline); window.addEventListener('online', onOnline);
onEmpty(); // this ensures that the loading screen is dismissed onEmpty(); // this ensures that the loading screen is dismissed
return; return;
} }
if (!window.Signal.Util.Registration.everDone()) { if (!window.Signal.Util.Registration.everDone()) {
return; return;
} }
preMessageReceiverStatus = WebSocket.CONNECTING;
if (messageReceiver) { if (messageReceiver) {
await messageReceiver.stopProcessing(); await messageReceiver.stopProcessing();
await window.waitForAllBatchers(); await window.waitForAllBatchers();
messageReceiver.unregisterBatchers(); messageReceiver.unregisterBatchers();
messageReceiver = null; messageReceiver = null;
} }
const OLD_USERNAME = storage.get('number_id'); const OLD_USERNAME = storage.get('number_id');
const USERNAME = storage.get('uuid_id'); const USERNAME = storage.get('uuid_id');
const PASSWORD = storage.get('password'); const PASSWORD = storage.get('password');
const mySignalingKey = storage.get('signaling_key'); const mySignalingKey = storage.get('signaling_key');
window.textsecure.messaging = new textsecure.MessageSender(
USERNAME || OLD_USERNAME,
PASSWORD
);
if (connectCount === 0) {
try {
// Force a re-fetch before we process our queue. We may want to turn on
something
// which changes how we process incoming messages!
await window.Signal.RemoteConfig.refreshRemoteConfig();
} catch (error) {
window.log.error(
'connect: Error refreshing remote config:',
error && error.stack ? error.stack : error
);
}
try {
if (window.Signal.RemoteConfig.isEnabled('desktop.cds')) {
const lonelyE164s = window
.getConversations()
.filter(
c =>
c.isPrivate() &&
c.get('e164') &&
!c.get('uuid') &&
!c.isEverUnregistered()
)
.map(c => c.get('e164'));
if (lonelyE164s.length > 0) {
const lookup = await textsecure.messaging.getUuidsForE164s(
lonelyE164s
);
const e164s = Object.keys(lookup);
e164s.forEach(e164 => {
const uuid = lookup[e164];
if (!uuid) {
const byE164 = window.ConversationController.get(e164);
if (byE164) {
byE164.setUnregistered();
}
}
window.ConversationController.ensureContactIds({
e164,
uuid,
highTrust: true,
});
});
}
}
} catch (error) {
window.log.error(
'connect: Error fetching UUIDs for lonely e164s:',
error && error.stack ? error.stack : error
);
}
}
connectCount += 1; connectCount += 1;
const options = { const options = {
retryCached: connectCount === 1, retryCached: connectCount === 1,
serverTrustRoot: window.getServerTrustRoot(), serverTrustRoot: window.getServerTrustRoot(),
}; };
Whisper.deliveryReceiptQueue.pause(); // avoid flood of delivery receipts un til we catch up Whisper.deliveryReceiptQueue.pause(); // avoid flood of delivery receipts un til we catch up
Whisper.Notifications.disable(); // avoid notification flood until empty Whisper.Notifications.disable(); // avoid notification flood until empty
// initialize the socket and start listening for messages // initialize the socket and start listening for messages
window.log.info('Initializing socket and listening for messages'); window.log.info('Initializing socket and listening for messages');
messageReceiver = new textsecure.MessageReceiver( messageReceiver = new textsecure.MessageReceiver(
OLD_USERNAME, OLD_USERNAME,
USERNAME, USERNAME,
PASSWORD, PASSWORD,
mySignalingKey, mySignalingKey,
options options
); );
window.textsecure.messageReceiver = messageReceiver; window.textsecure.messageReceiver = messageReceiver;
window.Signal.Services.initializeGroupCredentialFetcher();
preMessageReceiverStatus = null;
function addQueuedEventListener(name, handler) { function addQueuedEventListener(name, handler) {
messageReceiver.addEventListener(name, (...args) => messageReceiver.addEventListener(name, (...args) =>
eventHandlerQueue.add(async () => { eventHandlerQueue.add(async () => {
try { try {
await handler(...args); await handler(...args);
} finally { } finally {
// message/sent: Message.handleDataMessage has its own queue and wil l trigger // message/sent: Message.handleDataMessage has its own queue and wil l trigger
// this event itself when complete. // this event itself when complete.
// error: Error processing (below) also has its own queue and self-t rigger. // error: Error processing (below) also has its own queue and self-t rigger.
if (name !== 'message' && name !== 'sent' && name !== 'error') { if (name !== 'message' && name !== 'sent' && name !== 'error') {
skipping to change at line 1712 skipping to change at line 1850
addQueuedEventListener('messageRequestResponse', onMessageRequestResponse); addQueuedEventListener('messageRequestResponse', onMessageRequestResponse);
addQueuedEventListener('profileKeyUpdate', onProfileKeyUpdate); addQueuedEventListener('profileKeyUpdate', onProfileKeyUpdate);
addQueuedEventListener('fetchLatest', onFetchLatestSync); addQueuedEventListener('fetchLatest', onFetchLatestSync);
addQueuedEventListener('keys', onKeysSync); addQueuedEventListener('keys', onKeysSync);
window.Signal.AttachmentDownloads.start({ window.Signal.AttachmentDownloads.start({
getMessageReceiver: () => messageReceiver, getMessageReceiver: () => messageReceiver,
logger: window.log, logger: window.log,
}); });
window.textsecure.messaging = new textsecure.MessageSender(
USERNAME || OLD_USERNAME,
PASSWORD
);
if (connectCount === 1) { if (connectCount === 1) {
window.Signal.Stickers.downloadQueuedPacks(); window.Signal.Stickers.downloadQueuedPacks();
await window.textsecure.messaging.sendRequestKeySyncMessage(); await window.textsecure.messaging.sendRequestKeySyncMessage();
} }
// On startup after upgrading to a new version, request a contact sync // On startup after upgrading to a new version, request a contact sync
// (but only if we're not the primary device) // (but only if we're not the primary device)
if ( if (
!firstRun && !firstRun &&
connectCount === 1 && connectCount === 1 &&
skipping to change at line 1765 skipping to change at line 1898
await server.registerSupportForUnauthenticatedDelivery(); await server.registerSupportForUnauthenticatedDelivery();
storage.put(udSupportKey, true); storage.put(udSupportKey, true);
} catch (error) { } catch (error) {
window.log.error( window.log.error(
'Error: Unable to register for unauthenticated delivery support.', 'Error: Unable to register for unauthenticated delivery support.',
error && error.stack ? error.stack : error error && error.stack ? error.stack : error
); );
} }
} }
// TODO: uncomment this once we want to start registering UUID support const hasRegisteredGV2Support = 'hasRegisteredGV2Support';
// const hasRegisteredUuidSupportKey = 'hasRegisteredUuidSupport'; if (
// if ( !storage.get(hasRegisteredGV2Support) &&
// !storage.get(hasRegisteredUuidSupportKey) && textsecure.storage.user.getUuid()
// textsecure.storage.user.getUuid() ) {
// ) { const server = WebAPI.connect({
// const server = WebAPI.connect({ username: USERNAME || OLD_USERNAME,
// username: USERNAME || OLD_USERNAME, password: PASSWORD,
// password: PASSWORD, });
// }); try {
// try { await server.registerCapabilities({ gv2: true });
// await server.registerCapabilities({ uuid: true }); storage.put(hasRegisteredGV2Support, true);
// storage.put(hasRegisteredUuidSupportKey, true); } catch (error) {
// } catch (error) { window.log.error(
// window.log.error( 'Error: Unable to register support for GV2.',
// 'Error: Unable to register support for UUID messages.', error && error.stack ? error.stack : error
// error && error.stack ? error.stack : error );
// ); }
// } }
// }
const deviceId = textsecure.storage.user.getDeviceId(); const deviceId = textsecure.storage.user.getDeviceId();
// If we didn't capture a UUID on registration, go get it from the server // If we didn't capture a UUID on registration, go get it from the server
if (!textsecure.storage.user.getUuid()) { if (!textsecure.storage.user.getUuid()) {
const server = WebAPI.connect({ const server = WebAPI.connect({
username: OLD_USERNAME, username: OLD_USERNAME,
password: PASSWORD, password: PASSWORD,
}); });
try { try {
skipping to change at line 1873 skipping to change at line 2005
idleDetector.start(); idleDetector.start();
}); });
} }
function onChangeTheme() { function onChangeTheme() {
const view = window.owsDesktopApp.appView; const view = window.owsDesktopApp.appView;
if (view) { if (view) {
view.applyTheme(); view.applyTheme();
} }
} }
const FIVE_MINUTES = 5 * 60 * 1000;
// Note: once this function returns, there still might be messages being proce
ssed on
// a given conversation's queue. But we have processed all events from the w
ebsocket.
async function waitForEmptyEventQueue() {
if (!messageReceiver) {
window.log.info(
'waitForEmptyEventQueue: No messageReceiver available, returning early'
);
return;
}
if (!messageReceiver.hasEmptied()) {
window.log.info(
'waitForEmptyEventQueue: Waiting for MessageReceiver empty event...'
);
let resolve;
let reject;
const promise = new Promise((innerResolve, innerReject) => {
resolve = innerResolve;
reject = innerReject;
});
const timeout = setTimeout(reject, FIVE_MINUTES);
const onEmptyOnce = () => {
messageReceiver.removeEventListener('empty', onEmptyOnce);
clearTimeout(timeout);
resolve();
};
messageReceiver.addEventListener('empty', onEmptyOnce);
await promise;
}
window.log.info(
'waitForEmptyEventQueue: Waiting for event handler queue idle...'
);
await eventHandlerQueue.onIdle();
}
window.waitForEmptyEventQueue = waitForEmptyEventQueue;
async function onEmpty() { async function onEmpty() {
await Promise.all([ await Promise.all([
window.waitForAllBatchers(), window.waitForAllBatchers(),
window.waitForAllWaitBatchers(), window.waitForAllWaitBatchers(),
]); ]);
window.log.info('onEmpty: All outstanding database requests complete'); window.log.info('onEmpty: All outstanding database requests complete');
initialLoadComplete = true; initialLoadComplete = true;
window.readyForUpdates(); window.readyForUpdates();
// Start listeners here, after we get through our queue. // Start listeners here, after we get through our queue.
Whisper.RotateSignedPreKeyListener.init(Whisper.events, newVersion); Whisper.RotateSignedPreKeyListener.init(Whisper.events, newVersion);
window.Signal.RefreshSenderCertificate.initialize({ window.Signal.RefreshSenderCertificate.initialize({
events: Whisper.events, events: Whisper.events,
storage, storage,
navigator, navigator,
logger: window.log, logger: window.log,
}); });
// Force a re-fetch here when we've processed our queue. Without this, we wo
n't try
// again for two hours after our first attempt. Which might have been whil
e we were
// offline or didn't have credentials.
window.Signal.RemoteConfig.refreshRemoteConfig();
let interval = setInterval(() => { let interval = setInterval(() => {
const view = window.owsDesktopApp.appView; const view = window.owsDesktopApp.appView;
if (view) { if (view) {
clearInterval(interval); clearInterval(interval);
interval = null; interval = null;
view.onEmpty(); view.onEmpty();
} }
}, 500); }, 500);
Whisper.deliveryReceiptQueue.start(); Whisper.deliveryReceiptQueue.start();
skipping to change at line 1979 skipping to change at line 2149
if (linkPreviews === true || linkPreviews === false) { if (linkPreviews === true || linkPreviews === false) {
storage.put('linkPreviews', linkPreviews); storage.put('linkPreviews', linkPreviews);
} }
} }
function onTyping(ev) { function onTyping(ev) {
// Note: this type of message is automatically removed from cache in Message Receiver // Note: this type of message is automatically removed from cache in Message Receiver
const { typing, sender, senderUuid, senderDevice } = ev; const { typing, sender, senderUuid, senderDevice } = ev;
const { groupId, started } = typing || {}; const { groupId, groupV2Id, started } = typing || {};
// We don't do anything with incoming typing messages if the setting is disa bled // We don't do anything with incoming typing messages if the setting is disa bled
if (!storage.get('typingIndicators')) { if (!storage.get('typingIndicators')) {
return; return;
} }
const senderId = ConversationController.ensureContactIds({ const senderId = ConversationController.ensureContactIds({
e164: sender, e164: sender,
uuid: senderUuid, uuid: senderUuid,
highTrust: true, highTrust: true,
}); });
const conversation = ConversationController.get(groupId || senderId); const conversation = ConversationController.get(
groupV2Id || groupId || senderId
);
const ourId = ConversationController.getOurConversationId(); const ourId = ConversationController.getOurConversationId();
if (conversation) { if (!conversation) {
// We drop typing notifications in groups we're not a part of window.log.warn(
if (!conversation.isPrivate() && !conversation.hasMember(ourId)) { `onTyping: Did not find conversation for typing indicator (groupv2(${gro
window.log.warn( upV2Id}), group(${groupId}), ${sender}, ${senderUuid})`
`Received typing indicator for group ${conversation.idForLogging()}, w );
hich we're not a part of. Dropping.` return;
); }
return;
}
conversation.notifyTyping({ // We drop typing notifications in groups we're not a part of
isTyping: started, if (!conversation.isPrivate() && !conversation.hasMember(ourId)) {
isMe: ourId === senderId, window.log.warn(
sender, `Received typing indicator for group ${conversation.idForLogging()}, whi
senderUuid, ch we're not a part of. Dropping.`
senderId, );
senderDevice, return;
});
} }
conversation.notifyTyping({
isTyping: started,
isMe: ourId === senderId,
sender,
senderUuid,
senderId,
senderDevice,
});
} }
async function onStickerPack(ev) { async function onStickerPack(ev) {
ev.confirm(); ev.confirm();
const packs = ev.stickerPacks || []; const packs = ev.stickerPacks || [];
packs.forEach(pack => { packs.forEach(pack => {
const { id, key, isInstall, isRemove } = pack || {}; const { id, key, isInstall, isRemove } = pack || {};
skipping to change at line 2182 skipping to change at line 2359
window.log.info( window.log.info(
'onContactReceived: Adding the message history disclaimer on link' 'onContactReceived: Adding the message history disclaimer on link'
); );
await conversation.addMessageHistoryDisclaimer(); await conversation.addMessageHistoryDisclaimer();
} }
} catch (error) { } catch (error) {
window.log.error('onContactReceived error:', Errors.toLogFormat(error)); window.log.error('onContactReceived error:', Errors.toLogFormat(error));
} }
} }
// Note: this handler is only for v1 groups received via 'group sync' messages
async function onGroupReceived(ev) { async function onGroupReceived(ev) {
const details = ev.groupDetails; const details = ev.groupDetails;
const { id } = details; const { id } = details;
const idBuffer = window.Signal.Crypto.fromEncodedBinaryToArrayBuffer(id); const idBuffer = window.Signal.Crypto.fromEncodedBinaryToArrayBuffer(id);
const idBytes = idBuffer.byteLength; const idBytes = idBuffer.byteLength;
if (idBytes !== 16) { if (idBytes !== 16) {
window.log.error( window.log.error(
`onGroupReceived: Id was ${idBytes} bytes, expected 16 bytes. Dropping g roup.` `onGroupReceived: Id was ${idBytes} bytes, expected 16 bytes. Dropping g roup.`
); );
return; return;
} }
const conversation = await ConversationController.getOrCreateAndWait( const conversation = await ConversationController.getOrCreateAndWait(
id, id,
'group' 'group'
); );
if (conversation.get('groupVersion') > 1) {
window.log.warn(
'Got group sync for v2 group: ',
conversation.idForLoggoing()
);
return;
}
const memberConversations = details.membersE164.map(e164 => const memberConversations = details.membersE164.map(e164 =>
ConversationController.getOrCreate(e164, 'private') ConversationController.getOrCreate(e164, 'private')
); );
const members = memberConversations.map(c => c.get('id')); const members = memberConversations.map(c => c.get('id'));
const updates = { const updates = {
name: details.name, name: details.name,
members, members,
skipping to change at line 2276 skipping to change at line 2461
await conversation.updateExpirationTimer( await conversation.updateExpirationTimer(
expireTimer, expireTimer,
ConversationController.getOurConversationId(), ConversationController.getOurConversationId(),
receivedAt, receivedAt,
{ {
fromSync: true, fromSync: true,
} }
); );
} }
// Descriptors
const getGroupDescriptor = group => ({
type: Message.GROUP,
id: group.id,
});
// Matches event data from `libtextsecure` `MessageReceiver::handleSentMessage
`:
const getDescriptorForSent = ({ message, destination, destinationUuid }) =>
message.group
? getGroupDescriptor(message.group)
: {
type: Message.PRIVATE,
id: ConversationController.ensureContactIds({
e164: destination,
uuid: destinationUuid,
}),
};
// Matches event data from `libtextsecure` `MessageReceiver::handleDataMessage
`:
const getDescriptorForReceived = ({ message, source, sourceUuid }) =>
message.group
? getGroupDescriptor(message.group)
: {
type: Message.PRIVATE,
id: ConversationController.ensureContactIds({
e164: source,
uuid: sourceUuid,
highTrust: true,
}),
};
// Received: // Received:
async function handleMessageReceivedProfileUpdate({ async function handleMessageReceivedProfileUpdate({
data, data,
confirm, confirm,
messageDescriptor, messageDescriptor,
}) { }) {
const profileKey = data.message.profileKey.toString('base64'); const profileKey = data.message.profileKey.toString('base64');
const sender = await ConversationController.get(messageDescriptor.id); const sender = await ConversationController.get(messageDescriptor.id);
if (sender) { if (sender) {
// Will do the save for us // Will do the save for us
await sender.setProfileKey(profileKey); await sender.setProfileKey(profileKey);
} }
return confirm(); return confirm();
} }
// Matches event data from `libtextsecure` `MessageReceiver::handleDataMessage
`:
const getDescriptorForReceived = ({ message, source, sourceUuid }) => {
if (message.groupV2) {
const { id } = message.groupV2;
const conversationId = ConversationController.ensureGroup(id, {
groupVersion: 2,
masterKey: message.groupV2.masterKey,
secretParams: message.groupV2.secretParams,
publicParams: message.groupV2.publicParams,
});
return {
type: Message.GROUP,
id: conversationId,
};
}
if (message.group) {
const { id } = message.group;
const fromContactId = ConversationController.ensureContactIds({
e164: source,
uuid: sourceUuid,
highTrust: true,
});
const conversationId = ConversationController.ensureGroup(id, {
addedBy: fromContactId,
});
return {
type: Message.GROUP,
id: conversationId,
};
}
return {
type: Message.PRIVATE,
id: ConversationController.ensureContactIds({
e164: source,
uuid: sourceUuid,
highTrust: true,
}),
};
};
// Note: We do very little in this function, since everything in handleDataMes sage is // Note: We do very little in this function, since everything in handleDataMes sage is
// inside a conversation-specific queue(). Any code here might run before an earlier // inside a conversation-specific queue(). Any code here might run before an earlier
// message is processed in handleDataMessage(). // message is processed in handleDataMessage().
function onMessageReceived(event) { function onMessageReceived(event) {
const { data, confirm } = event; const { data, confirm } = event;
const messageDescriptor = getDescriptorForReceived(data); const messageDescriptor = getDescriptorForReceived(data);
const { PROFILE_KEY_UPDATE } = textsecure.protobuf.DataMessage.Flags; const { PROFILE_KEY_UPDATE } = textsecure.protobuf.DataMessage.Flags;
// eslint-disable-next-line no-bitwise // eslint-disable-next-line no-bitwise
skipping to change at line 2347 skipping to change at line 2545
data, data,
confirm, confirm,
messageDescriptor, messageDescriptor,
}); });
} }
const message = initIncomingMessage(data, messageDescriptor); const message = initIncomingMessage(data, messageDescriptor);
if (data.message.reaction) { if (data.message.reaction) {
const { reaction } = data.message; const { reaction } = data.message;
window.log.info('Queuing reaction for', reaction.targetTimestamp); window.log.info(
'Queuing incoming reaction for',
reaction.targetTimestamp
);
const reactionModel = Whisper.Reactions.add({ const reactionModel = Whisper.Reactions.add({
emoji: reaction.emoji, emoji: reaction.emoji,
remove: reaction.remove, remove: reaction.remove,
targetAuthorE164: reaction.targetAuthorE164, targetAuthorE164: reaction.targetAuthorE164,
targetAuthorUuid: reaction.targetAuthorUuid, targetAuthorUuid: reaction.targetAuthorUuid,
targetTimestamp: reaction.targetTimestamp.toNumber(), targetTimestamp: reaction.targetTimestamp,
timestamp: Date.now(), timestamp: Date.now(),
fromId: ConversationController.ensureContactIds({ fromId: ConversationController.ensureContactIds({
e164: data.source, e164: data.source,
uuid: data.sourceUuid, uuid: data.sourceUuid,
}), }),
}); });
// Note: We do not wait for completion here // Note: We do not wait for completion here
Whisper.Reactions.onReaction(reactionModel); Whisper.Reactions.onReaction(reactionModel);
confirm(); confirm();
return Promise.resolve(); return Promise.resolve();
} }
if (data.message.delete) { if (data.message.delete) {
const { delete: del } = data.message; const { delete: del } = data.message;
window.log.info('Queuing DOE for', del.targetSentTimestamp); window.log.info('Queuing incoming DOE for', del.targetSentTimestamp);
const deleteModel = Whisper.Deletes.add({ const deleteModel = Whisper.Deletes.add({
targetSentTimestamp: del.targetSentTimestamp, targetSentTimestamp: del.targetSentTimestamp,
serverTimestamp: data.serverTimestamp, serverTimestamp: data.serverTimestamp,
fromId: ConversationController.ensureContactIds({ fromId: ConversationController.ensureContactIds({
e164: data.source, e164: data.source,
uuid: data.sourceUuid, uuid: data.sourceUuid,
}), }),
}); });
// Note: We do not wait for completion here // Note: We do not wait for completion here
Whisper.Deletes.onDelete(deleteModel); Whisper.Deletes.onDelete(deleteModel);
skipping to change at line 2463 skipping to change at line 2664
if (data.unidentifiedStatus && data.unidentifiedStatus.length) { if (data.unidentifiedStatus && data.unidentifiedStatus.length) {
sentTo = data.unidentifiedStatus.map(item => item.destination); sentTo = data.unidentifiedStatus.map(item => item.destination);
const unidentified = _.filter(data.unidentifiedStatus, item => const unidentified = _.filter(data.unidentifiedStatus, item =>
Boolean(item.unidentified) Boolean(item.unidentified)
); );
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
data.unidentifiedDeliveries = unidentified.map(item => item.destination); data.unidentifiedDeliveries = unidentified.map(item => item.destination);
} }
const isGroup = descriptor.type === Message.GROUP;
const conversationId = isGroup
? ConversationController.ensureGroup(descriptor.id)
: descriptor.id;
return new Whisper.Message({ return new Whisper.Message({
source: textsecure.storage.user.getNumber(), source: textsecure.storage.user.getNumber(),
sourceUuid: textsecure.storage.user.getUuid(), sourceUuid: textsecure.storage.user.getUuid(),
sourceDevice: data.device, sourceDevice: data.device,
sent_at: data.timestamp, sent_at: data.timestamp,
serverTimestamp: data.serverTimestamp, serverTimestamp: data.serverTimestamp,
sent_to: sentTo, sent_to: sentTo,
received_at: now, received_at: now,
conversationId, conversationId: descriptor.id,
type: 'outgoing', type: 'outgoing',
sent: true, sent: true,
unidentifiedDeliveries: data.unidentifiedDeliveries || [], unidentifiedDeliveries: data.unidentifiedDeliveries || [],
expirationStartTimestamp: Math.min( expirationStartTimestamp: Math.min(
data.expirationStartTimestamp || data.timestamp || Date.now(), data.expirationStartTimestamp || data.timestamp || Date.now(),
Date.now() Date.now()
), ),
}); });
} }
// Matches event data from `libtextsecure` `MessageReceiver::handleSentMessage
`:
const getDescriptorForSent = ({ message, destination, destinationUuid }) => {
if (message.groupV2) {
const { id } = message.groupV2;
const conversationId = ConversationController.ensureGroup(id, {
groupVersion: 2,
masterKey: message.groupV2.masterKey,
secretParams: message.groupV2.secretParams,
publicParams: message.groupV2.publicParams,
});
return {
type: Message.GROUP,
id: conversationId,
};
}
if (message.group) {
const { id } = message.group;
const conversationId = ConversationController.ensureGroup(id);
return {
type: Message.GROUP,
id: conversationId,
};
}
return {
type: Message.PRIVATE,
id: ConversationController.ensureContactIds({
e164: destination,
uuid: destinationUuid,
highTrust: true,
}),
};
};
// Note: We do very little in this function, since everything in handleDataMes sage is // Note: We do very little in this function, since everything in handleDataMes sage is
// inside a conversation-specific queue(). Any code here might run before an earlier // inside a conversation-specific queue(). Any code here might run before an earlier
// message is processed in handleDataMessage(). // message is processed in handleDataMessage().
function onSentMessage(event) { function onSentMessage(event) {
const { data, confirm } = event; const { data, confirm } = event;
const messageDescriptor = getDescriptorForSent(data); const messageDescriptor = getDescriptorForSent(data);
const { PROFILE_KEY_UPDATE } = textsecure.protobuf.DataMessage.Flags; const { PROFILE_KEY_UPDATE } = textsecure.protobuf.DataMessage.Flags;
// eslint-disable-next-line no-bitwise // eslint-disable-next-line no-bitwise
skipping to change at line 2510 skipping to change at line 2742
data, data,
confirm, confirm,
messageDescriptor, messageDescriptor,
}); });
} }
const message = createSentMessage(data, messageDescriptor); const message = createSentMessage(data, messageDescriptor);
if (data.message.reaction) { if (data.message.reaction) {
const { reaction } = data.message; const { reaction } = data.message;
window.log.info('Queuing sent reaction for', reaction.targetTimestamp);
const reactionModel = Whisper.Reactions.add({ const reactionModel = Whisper.Reactions.add({
emoji: reaction.emoji, emoji: reaction.emoji,
remove: reaction.remove, remove: reaction.remove,
targetAuthorE164: reaction.targetAuthorE164, targetAuthorE164: reaction.targetAuthorE164,
targetAuthorUuid: reaction.targetAuthorUuid, targetAuthorUuid: reaction.targetAuthorUuid,
targetTimestamp: reaction.targetTimestamp.toNumber(), targetTimestamp: reaction.targetTimestamp,
timestamp: Date.now(), timestamp: Date.now(),
fromId: ConversationController.getOurConversationId(), fromId: ConversationController.getOurConversationId(),
fromSync: true, fromSync: true,
}); });
// Note: We do not wait for completion here // Note: We do not wait for completion here
Whisper.Reactions.onReaction(reactionModel); Whisper.Reactions.onReaction(reactionModel);
event.confirm(); event.confirm();
return Promise.resolve(); return Promise.resolve();
} }
if (data.message.delete) { if (data.message.delete) {
const { delete: del } = data.message; const { delete: del } = data.message;
window.log.info('Queuing sent DOE for', del.targetSentTimestamp);
const deleteModel = Whisper.Deletes.add({ const deleteModel = Whisper.Deletes.add({
targetSentTimestamp: del.targetSentTimestamp, targetSentTimestamp: del.targetSentTimestamp,
serverTimestamp: del.serverTimestamp, serverTimestamp: del.serverTimestamp,
fromId: ConversationController.getOurConversationId(), fromId: ConversationController.getOurConversationId(),
}); });
// Note: We do not wait for completion here // Note: We do not wait for completion here
Whisper.Deletes.onDelete(deleteModel); Whisper.Deletes.onDelete(deleteModel);
confirm(); confirm();
return Promise.resolve(); return Promise.resolve();
} }
// Don't wait for handleDataMessage, as it has its own per-conversation queu eing // Don't wait for handleDataMessage, as it has its own per-conversation queu eing
message.handleDataMessage(data.message, event.confirm, { message.handleDataMessage(data.message, event.confirm, {
data, data,
}); });
return Promise.resolve(); return Promise.resolve();
} }
function initIncomingMessage(data, descriptor) { function initIncomingMessage(data, descriptor) {
// Ensure that we have an accurate record for who this message is from
const fromContactId = ConversationController.ensureContactIds({
e164: data.source,
uuid: data.sourceUuid,
highTrust: true,
});
const isGroup = descriptor.type === Message.GROUP;
const conversationId = isGroup
? ConversationController.ensureGroup(descriptor.id, {
addedBy: fromContactId,
})
: fromContactId;
return new Whisper.Message({ return new Whisper.Message({
source: data.source, source: data.source,
sourceUuid: data.sourceUuid, sourceUuid: data.sourceUuid,
sourceDevice: data.sourceDevice, sourceDevice: data.sourceDevice,
sent_at: data.timestamp, sent_at: data.timestamp,
serverTimestamp: data.serverTimestamp, serverTimestamp: data.serverTimestamp,
received_at: Date.now(), received_at: Date.now(),
conversationId, conversationId: descriptor.id,
unidentifiedDeliveryReceived: data.unidentifiedDeliveryReceived, unidentifiedDeliveryReceived: data.unidentifiedDeliveryReceived,
type: 'incoming', type: 'incoming',
unread: 1, unread: 1,
}); });
} }
async function unlinkAndDisconnect() { async function unlinkAndDisconnect() {
Whisper.events.trigger('unauthorized'); Whisper.events.trigger('unauthorized');
if (messageReceiver) { if (messageReceiver) {
skipping to change at line 2684 skipping to change at line 2904
type: Message.PRIVATE, type: Message.PRIVATE,
id: ConversationController.ensureContactIds({ id: ConversationController.ensureContactIds({
e164: envelope.source, e164: envelope.source,
uuid: envelope.sourceUuid, uuid: envelope.sourceUuid,
}), }),
}); });
const conversationId = message.get('conversationId'); const conversationId = message.get('conversationId');
const conversation = ConversationController.get(conversationId); const conversation = ConversationController.get(conversationId);
if (!conversation) {
window.log.warn(
'onError: No conversation id, cannot save error bubble'
);
ev.confirm();
return Promise.resolve();
}
// This matches the queueing behavior used in Message.handleDataMessage // This matches the queueing behavior used in Message.handleDataMessage
conversation.queueJob(async () => { conversation.queueJob(async () => {
const existingMessage = await window.Signal.Data.getMessageBySender( const existingMessage = await window.Signal.Data.getMessageBySender(
message.attributes, message.attributes,
{ {
Message: Whisper.Message, Message: Whisper.Message,
} }
); );
if (existingMessage) { if (existingMessage) {
ev.confirm(); ev.confirm();
skipping to change at line 2773 skipping to change at line 3001
const FETCH_LATEST_ENUM = textsecure.protobuf.SyncMessage.FetchLatest.Type; const FETCH_LATEST_ENUM = textsecure.protobuf.SyncMessage.FetchLatest.Type;
switch (eventType) { switch (eventType) {
case FETCH_LATEST_ENUM.LOCAL_PROFILE: case FETCH_LATEST_ENUM.LOCAL_PROFILE:
// Intentionally do nothing since we'll be receiving the storage manifes t request // Intentionally do nothing since we'll be receiving the storage manifes t request
// and will update local profile along with that. // and will update local profile along with that.
break; break;
case FETCH_LATEST_ENUM.STORAGE_MANIFEST: case FETCH_LATEST_ENUM.STORAGE_MANIFEST:
window.log.info('onFetchLatestSync: fetching latest manifest'); window.log.info('onFetchLatestSync: fetching latest manifest');
await window.Signal.Util.runStorageServiceSyncJob(); await window.Signal.Services.runStorageServiceSyncJob();
break; break;
default: default:
window.log.info( window.log.info(
`onFetchLatestSync: Unknown type encountered ${eventType}` `onFetchLatestSync: Unknown type encountered ${eventType}`
); );
} }
} }
async function onKeysSync(ev) { async function onKeysSync(ev) {
ev.confirm(); ev.confirm();
const { storageServiceKey } = ev; const { storageServiceKey } = ev;
if (storageServiceKey === null) {
window.log.info('onKeysSync: deleting storageKey');
storage.remove('storageKey');
}
if (storageServiceKey) { if (storageServiceKey) {
window.log.info('onKeysSync: received keys'); window.log.info('onKeysSync: received keys');
const storageServiceKeyBase64 = window.Signal.Crypto.arrayBufferToBase64( const storageServiceKeyBase64 = window.Signal.Crypto.arrayBufferToBase64(
storageServiceKey storageServiceKey
); );
storage.put('storageKey', storageServiceKeyBase64); storage.put('storageKey', storageServiceKeyBase64);
await window.Signal.Util.runStorageServiceSyncJob(); await window.Signal.Services.runStorageServiceSyncJob();
} }
} }
async function onMessageRequestResponse(ev) { async function onMessageRequestResponse(ev) {
ev.confirm(); ev.confirm();
const { threadE164, threadUuid, groupId, messageRequestResponseType } = ev; const { threadE164, threadUuid, groupId, messageRequestResponseType } = ev;
const args = { const args = {
threadE164, threadE164,
 End of changes. 40 change blocks. 
116 lines changed or deleted 353 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)