diff --git a/js/models/conversations.js b/js/models/conversations.js index ba9a8bbf7..7cb29d34c 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -253,11 +253,7 @@ this.trigger('change', this); this.messageCollection.forEach(m => m.trigger('change')); this.updateTextInputState(); - if (this.isPrivate()) { - await textsecure.messaging.sendContactSyncMessage([this]); - } else { - await textsecure.messaging.sendGroupSyncMessage([this]); - } + await textsecure.messaging.sendBlockedListSyncMessage(); }, async unblock() { if (!this.id || this.isPublic() || this.isRss()) { @@ -270,11 +266,7 @@ this.trigger('change', this); this.messageCollection.forEach(m => m.trigger('change')); this.updateTextInputState(); - if (this.isPrivate()) { - await textsecure.messaging.sendContactSyncMessage([this]); - } else { - await textsecure.messaging.sendGroupSyncMessage([this]); - } + await textsecure.messaging.sendBlockedListSyncMessage(); }, setMessageSelectionBackdrop() { const messageSelected = this.selectedMessages.size > 0; diff --git a/libtextsecure/sendmessage.js b/libtextsecure/sendmessage.js index f31114f99..aac069dec 100644 --- a/libtextsecure/sendmessage.js +++ b/libtextsecure/sendmessage.js @@ -453,6 +453,33 @@ MessageSender.prototype = { return libsession.getMessageQueue().sendSyncMessage(openGroupsSyncMessage); }, + async sendBlockedListSyncMessage() { + // If we havn't got a primaryDeviceKey then we are in the middle of pairing + // primaryDevicePubKey is set to our own number if we are the master device + const primaryDeviceKey = window.storage.get('primaryDevicePubKey'); + if (!primaryDeviceKey) { + return Promise.resolve(); + } + const convos = window.getConversations().models; + + const conversations = Array.isArray(convos) ? convos : [convos]; + + const { + blockedNumbersConvos, + blockedGroupsConvos, + } = await libsession.Utils.SyncMessageUtils.filterBlockedNumbers( + conversations + ); + + const blockedSyncMessage = new libsession.Messages.Outgoing.BlockedListSyncMessage( + { + timestamp: Date.now(), + numbers: blockedNumbersConvos.map(n => n.id), + groups: blockedGroupsConvos.map(g => g.id), + } + ); + return libsession.getMessageQueue().sendSyncMessage(blockedSyncMessage); + }, syncReadMessages(reads) { const myDevice = textsecure.storage.user.getDeviceId(); // FIXME currently not in used @@ -521,6 +548,9 @@ textsecure.MessageSender = function MessageSenderWrapper(username, password) { this.syncVerification = sender.syncVerification.bind(sender); this.makeProxiedRequest = sender.makeProxiedRequest.bind(sender); this.getProxiedSize = sender.getProxiedSize.bind(sender); + this.sendBlockedListSyncMessage = sender.sendBlockedListSyncMessage.bind( + sender + ); }; textsecure.MessageSender.prototype = { diff --git a/ts/receiver/syncMessages.ts b/ts/receiver/syncMessages.ts index 94b56e432..85164ffbd 100644 --- a/ts/receiver/syncMessages.ts +++ b/ts/receiver/syncMessages.ts @@ -17,6 +17,8 @@ import { handleContacts } from './multidevice'; import { onGroupReceived } from './groups'; import { MultiDeviceProtocol } from '../session/protocols'; import { DataMessage } from '../session/messages/outgoing'; +import { BlockedNumberController } from '../util'; +import { StringUtils } from '../session/utils'; export async function handleSyncMessage( envelope: EnvelopePlus, @@ -36,6 +38,14 @@ export async function handleSyncMessage( ); } + // remove empty fields (generated by ts even if they should be null) + if (syncMessage.openGroups && !syncMessage.openGroups.length) { + syncMessage.openGroups = null; + } + if (syncMessage.read && !syncMessage.read.length) { + syncMessage.read = null; + } + if (syncMessage.sent) { const sentMessage = syncMessage.sent; const message = sentMessage.message as SignalService.IDataMessage; @@ -166,16 +176,64 @@ async function handleBlocked( blocked: SignalService.SyncMessage.IBlocked ) { window.log.info('Setting these numbers as blocked:', blocked.numbers); - window.textsecure.storage.put('blocked', blocked.numbers); - const groupIds = _.map(blocked.groupIds, (groupId: any) => - groupId.toBinary() - ); - window.log.info( - 'Setting these groups as blocked:', - groupIds.map((groupId: any) => `group(${groupId})`) - ); - window.textsecure.storage.put('blocked-groups', groupIds); + async function fetchAndRefreshConversation(n: string) { + const conv = await window.ConversationController.get(n); + if (conv) { + conv.trigger('change', conv); + } + } + + if (blocked.numbers) { + const currentlyBlockedNumbers = BlockedNumberController.getBlockedNumbers(); + const toRemoveFromBlocked = _.difference( + currentlyBlockedNumbers, + blocked.numbers + ); + const toAddToBlocked = _.difference( + blocked.numbers, + currentlyBlockedNumbers + ); + + await Promise.all( + toAddToBlocked.map(async n => { + await BlockedNumberController.block(n); + await fetchAndRefreshConversation(n); + }) + ); + await Promise.all( + toRemoveFromBlocked.map(async n => { + await BlockedNumberController.unblock(n); + await fetchAndRefreshConversation(n); + }) + ); + } + + if (blocked.groupIds) { + const groupIds = _.map(blocked.groupIds, (groupId: any) => + StringUtils.decode(groupId, 'utf8') + ); + window.log.info( + 'Setting these groups as blocked:', + groupIds.map((groupId: any) => `group(${groupId})`) + ); + const currentlyBlockedGroups = BlockedNumberController.getBlockedGroups(); + const toRemoveFromBlocked = _.difference(currentlyBlockedGroups, groupIds); + const toAddToBlocked = _.difference(groupIds, currentlyBlockedGroups); + + await Promise.all( + toAddToBlocked.map(async n => { + await BlockedNumberController.blockGroup(n); + await fetchAndRefreshConversation(n); + }) + ); + await Promise.all( + toRemoveFromBlocked.map(async n => { + await BlockedNumberController.unblockGroup(n); + await fetchAndRefreshConversation(n); + }) + ); + } await removeFromCache(envelope); } diff --git a/ts/session/messages/outgoing/content/sync/BlockedListSyncMessage.ts b/ts/session/messages/outgoing/content/sync/BlockedListSyncMessage.ts new file mode 100644 index 000000000..d116c39aa --- /dev/null +++ b/ts/session/messages/outgoing/content/sync/BlockedListSyncMessage.ts @@ -0,0 +1,42 @@ +import { SyncMessage } from './SyncMessage'; +import { SignalService } from '../../../../../protobuf'; +import { MessageParams } from '../../Message'; +import { StringUtils } from '../../../../utils'; + +interface BlockedListSyncMessageParams extends MessageParams { + groups: Array; + numbers: Array; +} + +export abstract class BlockedListSyncMessage extends SyncMessage { + public readonly groups: Array; + public readonly numbers: Array; + + constructor(params: BlockedListSyncMessageParams) { + super({ timestamp: params.timestamp, identifier: params.identifier }); + this.groups = params.groups.map(g => { + if (typeof g !== 'string') { + throw new TypeError( + `invalid group id (expected string) found:${typeof g}` + ); + } + return new Uint8Array(StringUtils.encode(g, 'utf8')); + }); + if (params.numbers.length && typeof params.numbers[0] !== 'string') { + throw new TypeError( + `invalid number (expected string) found:${typeof params.numbers[0]}` + ); + } + this.numbers = params.numbers; + } + + protected syncProto(): SignalService.SyncMessage { + const syncMessage = super.syncProto(); + syncMessage.blocked = new SignalService.SyncMessage.Blocked({ + groupIds: this.groups, + numbers: this.numbers, + }); + + return syncMessage; + } +} diff --git a/ts/session/messages/outgoing/content/sync/index.ts b/ts/session/messages/outgoing/content/sync/index.ts index a2aea70b9..bfb24b2a9 100644 --- a/ts/session/messages/outgoing/content/sync/index.ts +++ b/ts/session/messages/outgoing/content/sync/index.ts @@ -6,3 +6,4 @@ export * from './SyncMessage'; export * from './SentSyncMessage'; export * from './SyncReadMessage'; export * from './VerifiedSyncMessage'; +export * from './BlockedListSyncMessage'; diff --git a/ts/session/utils/SyncMessage.ts b/ts/session/utils/SyncMessage.ts index 85df60a86..8df5b5f41 100644 --- a/ts/session/utils/SyncMessage.ts +++ b/ts/session/utils/SyncMessage.ts @@ -95,6 +95,28 @@ export async function filterOpenGroupsConvos( ); } +export async function filterBlockedNumbers( + conversations: Array +): Promise { + // If we haven't got a primaryDeviceKey then we are in the middle of pairing + // primaryDevicePubKey is set to our own number if we are the master device + const thisDevice = await UserUtil.getCurrentDevicePubKey(); + + if (!thisDevice) { + return { blockedNumbers: [], blockedGroupsIds: [] }; + } + + const blockedNumbersConvos = conversations.filter( + c => c.isPrivate() && c.isBlocked() + ); + + const blockedGroupsConvos = conversations.filter( + c => c.isClosedGroup() && c.isBlocked() + ); + + return { blockedNumbersConvos, blockedGroupsConvos }; +} + // Serialise as ... // This is an implementation of the reciprocal of contacts_parser.js export function serialiseByteBuffers(buffers: Array): ByteBuffer {