diff --git a/package.json b/package.json index 6670e7dfa71e..d653e9aaad46 100644 --- a/package.json +++ b/package.json @@ -189,7 +189,7 @@ "@electron/fuses": "1.5.0", "@formatjs/intl": "2.6.7", "@mixer/parallel-prettier": "2.0.3", - "@signalapp/mock-server": "3.2.1", + "@signalapp/mock-server": "3.2.3", "@storybook/addon-a11y": "6.5.6", "@storybook/addon-actions": "6.5.6", "@storybook/addon-controls": "6.5.6", diff --git a/protos/SignalService.proto b/protos/SignalService.proto index aa49b1e40a59..d8382be35795 100644 --- a/protos/SignalService.proto +++ b/protos/SignalService.proto @@ -315,10 +315,10 @@ message DataMessage { STRIKETHROUGH = 4; MONOSPACE = 5; } - + optional uint32 start = 1; optional uint32 length = 2; - + oneof associatedValue { string mentionUuid = 3; Style style = 4; @@ -493,10 +493,6 @@ message SyncMessage { optional bool complete = 2 [default = false]; } - message Groups { - optional AttachmentPointer blob = 1; - } - message Blocked { repeated string numbers = 1; repeated string uuids = 3; @@ -507,7 +503,7 @@ message SyncMessage { enum Type { UNKNOWN = 0; CONTACTS = 1; - GROUPS = 2; + reserved /* GROUPS */ 2; BLOCKED = 3; CONFIGURATION = 4; KEYS = 5; @@ -623,7 +619,7 @@ message SyncMessage { optional Sent sent = 1; optional Contacts contacts = 2; - optional Groups groups = 3; + reserved /* groups */ 3; optional Request request = 4; repeated Read read = 5; optional Blocked blocked = 6; @@ -695,29 +691,6 @@ message ContactDetails { optional uint32 inboxPosition = 10; } -message GroupDetails { - message Avatar { - optional string contentType = 1; - optional uint32 length = 2; - } - - message Member { - optional string uuid = 1; - optional string e164 = 2; - } - - optional bytes id = 1; - optional string name = 2; - repeated string membersE164 = 3; - repeated Member members = 9; - optional Avatar avatar = 4; - optional bool active = 5 [default = true]; - optional uint32 expireTimer = 6; - optional string color = 7; - optional bool blocked = 8; - optional uint32 inboxPosition = 10; -} - message PniSignatureMessage { optional bytes pni = 1; // Signature *by* the PNI identity key *of* the ACI identity key diff --git a/ts/background.ts b/ts/background.ts index c26b2d7e0a2b..6dd2f8aaa94c 100644 --- a/ts/background.ts +++ b/ts/background.ts @@ -22,7 +22,6 @@ import createTaskWithTimeout, { } from './textsecure/TaskWithTimeout'; import type { MessageAttributesType, - ConversationAttributesType, ReactionAttributesType, } from './model-types.d'; import * as Bytes from './Bytes'; @@ -87,7 +86,6 @@ import type { EnvelopeUnsealedEvent, ErrorEvent, FetchLatestEvent, - GroupEvent, InvalidPlaintextEvent, KeysEvent, MessageEvent, @@ -108,7 +106,7 @@ import type { import type { WebAPIType } from './textsecure/WebAPI'; import * as KeyChangeListener from './textsecure/KeyChangeListener'; import { UpdateKeysListener } from './textsecure/UpdateKeysListener'; -import { isDirectConversation, isGroupV2 } from './util/whatTypeOfConversation'; +import { isDirectConversation } from './util/whatTypeOfConversation'; import { BackOff, FIBONACCI_TIMEOUTS } from './util/BackOff'; import { AppViewType } from './state/ducks/app'; import type { BadgesStateType } from './state/ducks/badges'; @@ -137,7 +135,6 @@ import { ReadStatus } from './messages/MessageReadStatus'; import type { SendStateByConversationId } from './messages/MessageSendState'; import { SendStatus } from './messages/MessageSendState'; import * as AttachmentDownloads from './messageModifiers/AttachmentDownloads'; -import * as Conversation from './types/Conversation'; import * as Stickers from './types/Stickers'; import * as Errors from './types/errors'; import { SignalService as Proto } from './protobuf'; @@ -359,14 +356,6 @@ export async function startApp(): Promise { 'contactSync', queuedEventListener(onContactSync) ); - messageReceiver.addEventListener( - 'group', - queuedEventListener(onGroupReceived) - ); - messageReceiver.addEventListener( - 'groupSync', - queuedEventListener(onGroupSyncComplete) - ); messageReceiver.addEventListener( 'sent', queuedEventListener(onSentMessage, false) @@ -573,12 +562,7 @@ export async function startApp(): Promise { window.setImmediate = window.nodeSetImmediate; const { Message } = window.Signal.Types; - const { - upgradeMessageSchema, - writeNewAttachmentData, - deleteAttachmentData, - doesAttachmentExist, - } = window.Signal.Migrations; + const { upgradeMessageSchema } = window.Signal.Migrations; log.info('background page reloaded'); log.info('environment:', window.getEnvironment()); @@ -1954,11 +1938,10 @@ export async function startApp(): Promise { MessageSender.getRequestConfigurationSyncMessage() ), singleProtoJobQueue.add(MessageSender.getRequestBlockSyncMessage()), - singleProtoJobQueue.add(MessageSender.getRequestGroupSyncMessage()), + runStorageService(), singleProtoJobQueue.add( MessageSender.getRequestContactSyncMessage() ), - runStorageService(), ]); } catch (error) { log.error( @@ -2311,85 +2294,6 @@ export async function startApp(): Promise { }); } - async function onGroupSyncComplete(): Promise { - log.info('onGroupSyncComplete'); - await window.storage.put('synced_at', Date.now()); - } - - // Note: this handler is only for v1 groups received via 'group sync' messages - async function onGroupReceived(ev: GroupEvent): Promise { - const details = ev.groupDetails; - const { id } = details; - - const conversation = await window.ConversationController.getOrCreateAndWait( - id, - 'group' - ); - if (isGroupV2(conversation.attributes)) { - log.warn('Got group sync for v2 group: ', conversation.idForLogging()); - return; - } - - const memberConversations = details.membersE164.map(e164 => - window.ConversationController.getOrCreate(e164, 'private') - ); - - const members = memberConversations.map(c => c.get('id')); - - const updates: Partial = { - name: details.name, - members, - type: 'group', - inbox_position: details.inboxPosition, - }; - - if (details.active) { - updates.left = false; - } else { - updates.left = true; - } - - if (details.blocked) { - conversation.block(); - } else { - conversation.unblock(); - } - - conversation.set(updates); - - // Update the conversation avatar only if new avatar exists and hash differs - const { avatar } = details; - if (avatar && avatar.data) { - const newAttributes = await Conversation.maybeUpdateAvatar( - conversation.attributes, - avatar.data, - { - writeNewAttachmentData, - deleteAttachmentData, - doesAttachmentExist, - } - ); - conversation.set(newAttributes); - } - - window.Signal.Data.updateConversation(conversation.attributes); - - const { expireTimer } = details; - const isValidExpireTimer = typeof expireTimer === 'number'; - if (!isValidExpireTimer) { - return; - } - - await conversation.updateExpirationTimer(expireTimer, { - // Note: because it's our conversationId, this notification will be marked read. But - // setting this will make 'isSetByOther' check true. - source: window.ConversationController.getOurConversationId(), - fromSync: true, - receivedAt: ev.receivedAtCounter, - reason: 'group sync', - }); - } - // Received: async function handleMessageReceivedProfileUpdate({ data, diff --git a/ts/test-both/ContactsParser_test.ts b/ts/test-both/ContactsParser_test.ts index 5fa21f1df90e..78b5b9482015 100644 --- a/ts/test-both/ContactsParser_test.ts +++ b/ts/test-both/ContactsParser_test.ts @@ -6,7 +6,7 @@ import protobuf from '../protobuf/wrap'; import * as Bytes from '../Bytes'; import { SignalService as Proto } from '../protobuf'; -import { ContactBuffer, GroupBuffer } from '../textsecure/ContactsParser'; +import { ContactBuffer } from '../textsecure/ContactsParser'; const { Writer } = protobuf; @@ -70,58 +70,4 @@ describe('ContactsParser', () => { assert.strictEqual(count, 3); }); }); - - describe('GroupBuffer', () => { - function getTestBuffer(): Uint8Array { - const avatarBuffer = generateAvatar(); - - const groupInfoBuffer = Proto.GroupDetails.encode({ - id: new Uint8Array([1, 3, 3, 7]), - name: 'Hackers', - membersE164: ['cereal', 'burn', 'phreak', 'joey'], - avatar: { contentType: 'image/jpeg', length: avatarBuffer.length }, - }).finish(); - - const writer = new Writer(); - writer.bytes(groupInfoBuffer); - const prefixedGroup = writer.finish(); - - const chunks: Array = []; - for (let i = 0; i < 3; i += 1) { - chunks.push(prefixedGroup); - chunks.push(avatarBuffer); - } - - return Bytes.concatenate(chunks); - } - - it('parses an array buffer of groups', () => { - const bytes = getTestBuffer(); - const groupBuffer = new GroupBuffer(bytes); - let group = groupBuffer.next(); - let count = 0; - while (group !== undefined) { - count += 1; - assert.strictEqual(group.name, 'Hackers'); - assert.deepEqual(group.id, new Uint8Array([1, 3, 3, 7])); - assert.sameMembers(group.membersE164, [ - 'cereal', - 'burn', - 'phreak', - 'joey', - ]); - assert.strictEqual(group.avatar?.contentType, 'image/jpeg'); - assert.strictEqual(group.avatar?.length, 255); - assert.strictEqual(group.avatar?.data.byteLength, 255); - const avatarBytes = new Uint8Array( - group.avatar?.data || new Uint8Array(0) - ); - for (let j = 0; j < 255; j += 1) { - assert.strictEqual(avatarBytes[j], j); - } - group = groupBuffer.next(); - } - assert.strictEqual(count, 3); - }); - }); }); diff --git a/ts/test-mock/messaging/edit_test.ts b/ts/test-mock/messaging/edit_test.ts index a5ed5feb1dbe..491da4bfdfc7 100644 --- a/ts/test-mock/messaging/edit_test.ts +++ b/ts/test-mock/messaging/edit_test.ts @@ -72,7 +72,7 @@ describe('editing', function needsName() { await bootstrap.teardown(); }); - it('handles outgoing edited messages phone -> desktop', async () => { + it('handles outgoing edited messages phone to desktop', async () => { const { phone, desktop } = bootstrap; const window = await app.getWindow(); @@ -130,7 +130,7 @@ describe('editing', function needsName() { assert.strictEqual(await messages.count(), 1, 'message count'); }); - it('handles incoming edited messages contact -> desktop', async () => { + it('handles incoming edited messages contact to desktop', async () => { const { contacts, desktop } = bootstrap; const window = await app.getWindow(); diff --git a/ts/textsecure/ContactsParser.ts b/ts/textsecure/ContactsParser.ts index a73472bb87ce..ecd58bc317d8 100644 --- a/ts/textsecure/ContactsParser.ts +++ b/ts/textsecure/ContactsParser.ts @@ -31,8 +31,6 @@ type MessageWithAvatar = Omit< expireTimer?: DurationInSeconds; }; -export type ModifiedGroupDetails = MessageWithAvatar; - export type ModifiedContactDetails = MessageWithAvatar; /* eslint-disable @typescript-eslint/brace-style -- Prettier conflicts with ESLint */ @@ -107,41 +105,6 @@ abstract class ParserBase< } } -export class GroupBuffer extends ParserBase< - Proto.GroupDetails, - typeof Proto.GroupDetails, - ModifiedGroupDetails -> { - constructor(arrayBuffer: Uint8Array) { - super(arrayBuffer, Proto.GroupDetails); - } - - public override next(): ModifiedGroupDetails | undefined { - const proto = this.decodeDelimited(); - if (!proto) { - return undefined; - } - - if (!proto.members) { - return proto; - } - - return { - ...proto, - members: proto.members.map((member, i) => { - if (!member.uuid) { - return member; - } - - return { - ...member, - uuid: normalizeUuid(member.uuid, `GroupBuffer.member[${i}].uuid`), - }; - }), - }; - } -} - export class ContactBuffer extends ParserBase< Proto.ContactDetails, typeof Proto.ContactDetails, diff --git a/ts/textsecure/MessageReceiver.ts b/ts/textsecure/MessageReceiver.ts index 816ea1af0bf2..3ad1ac6fbed3 100644 --- a/ts/textsecure/MessageReceiver.ts +++ b/ts/textsecure/MessageReceiver.ts @@ -72,7 +72,7 @@ import type { EventHandler } from './EventTarget'; import EventTarget from './EventTarget'; import { downloadAttachment } from './downloadAttachment'; import type { IncomingWebSocketRequest } from './WebsocketResources'; -import { ContactBuffer, GroupBuffer } from './ContactsParser'; +import { ContactBuffer } from './ContactsParser'; import type { WebAPIType } from './WebAPI'; import type { Storage } from './Storage'; import { WarnOnlyError } from './Errors'; @@ -112,8 +112,6 @@ import { ReadSyncEvent, ViewSyncEvent, ContactSyncEvent, - GroupEvent, - GroupSyncEvent, StoryRecipientUpdateEvent, CallEventSyncEvent, } from './messageReceiverEvents'; @@ -622,16 +620,6 @@ export default class MessageReceiver handler: (ev: ContactSyncEvent) => void ): void; - public override addEventListener( - name: 'group', - handler: (ev: GroupEvent) => void - ): void; - - public override addEventListener( - name: 'groupSync', - handler: (ev: GroupSyncEvent) => void - ): void; - public override addEventListener( name: 'envelopeQueued', handler: (ev: EnvelopeQueuedEvent) => void @@ -2998,10 +2986,6 @@ export default class MessageReceiver // before moving on since it updates conversation state. return this.handleContacts(envelope, syncMessage.contacts); } - if (syncMessage.groups) { - void this.handleGroups(envelope, syncMessage.groups); - return; - } if (syncMessage.blocked) { return this.handleBlocked(envelope, syncMessage.blocked); } @@ -3454,63 +3438,6 @@ export default class MessageReceiver log.info('handleContacts: finished'); } - private async handleGroups( - envelope: ProcessedEnvelope, - groups: Proto.SyncMessage.IGroups - ): Promise { - const logId = getEnvelopeId(envelope); - log.info('group sync'); - log.info(`MessageReceiver: handleGroups ${logId}`); - const { blob } = groups; - - this.removeFromCache(envelope); - - logUnexpectedUrgentValue(envelope, 'groupSync'); - - if (!blob) { - throw new Error('MessageReceiver.handleGroups: blob field was missing'); - } - - // Note: we do not return here because we don't want to block the next message on - // this attachment download and a lot of processing of that attachment. - const attachmentPointer = await this.handleAttachment(blob, { - disableRetries: true, - timeout: 90 * SECOND, - }); - const groupBuffer = new GroupBuffer(attachmentPointer.data); - let groupDetails = groupBuffer.next(); - const promises = []; - while (groupDetails) { - const { id } = groupDetails; - strictAssert(id, 'Group details without id'); - - if (id.byteLength !== 16) { - log.error( - `onGroupReceived: Id was ${id} bytes, expected 16 bytes. Dropping group.` - ); - continue; - } - - const ev = new GroupEvent( - { - ...groupDetails, - id: Bytes.toBinary(id), - }, - envelope.receivedAtCounter - ); - const promise = this.dispatchAndWait(logId, ev).catch(e => { - log.error('error processing group', e); - }); - groupDetails = groupBuffer.next(); - promises.push(promise); - } - - await Promise.all(promises); - - const ev = new GroupSyncEvent(); - return this.dispatchAndWait(logId, ev); - } - private async handleBlocked( envelope: ProcessedEnvelope, blocked: Proto.SyncMessage.IBlocked diff --git a/ts/textsecure/SendMessage.ts b/ts/textsecure/SendMessage.ts index c09973a7f93d..7599b4ef0d94 100644 --- a/ts/textsecure/SendMessage.ts +++ b/ts/textsecure/SendMessage.ts @@ -1367,30 +1367,6 @@ export default class MessageSender { }; } - static getRequestGroupSyncMessage(): SingleProtoJobData { - const myUuid = window.textsecure.storage.user.getCheckedUuid(); - - const request = new Proto.SyncMessage.Request(); - request.type = Proto.SyncMessage.Request.Type.GROUPS; - const syncMessage = this.createSyncMessage(); - syncMessage.request = request; - const contentMessage = new Proto.Content(); - contentMessage.syncMessage = syncMessage; - - const { ContentHint } = Proto.UnidentifiedSenderMessage.Message; - - return { - contentHint: ContentHint.RESENDABLE, - identifier: myUuid.toString(), - isSyncMessage: true, - protoBase64: Bytes.toBase64( - Proto.Content.encode(contentMessage).finish() - ), - type: 'groupSyncRequest', - urgent: false, - }; - } - static getRequestContactSyncMessage(): SingleProtoJobData { const myUuid = window.textsecure.storage.user.getCheckedUuid(); diff --git a/ts/textsecure/SyncRequest.ts b/ts/textsecure/SyncRequest.ts index a62bcc61b075..64bb07dd70db 100644 --- a/ts/textsecure/SyncRequest.ts +++ b/ts/textsecure/SyncRequest.ts @@ -7,7 +7,7 @@ import type { EventHandler } from './EventTarget'; import EventTarget from './EventTarget'; import MessageReceiver from './MessageReceiver'; -import type { ContactSyncEvent, GroupSyncEvent } from './messageReceiverEvents'; +import type { ContactSyncEvent } from './messageReceiverEvents'; import MessageSender from './SendMessage'; import { assertDev } from '../util/assert'; import * as log from '../logging/log'; @@ -19,14 +19,10 @@ class SyncRequestInner extends EventTarget { contactSync?: boolean; - groupSync?: boolean; - timeout: any; oncontact: (event: ContactSyncEvent) => void; - ongroup: (event: GroupSyncEvent) => void; - timeoutMillis: number; constructor(private receiver: MessageReceiver, timeoutMillis?: number) { @@ -41,9 +37,6 @@ class SyncRequestInner extends EventTarget { this.oncontact = this.onContactSyncComplete.bind(this); receiver.addEventListener('contactSync', this.oncontact); - this.ongroup = this.onGroupSyncComplete.bind(this); - receiver.addEventListener('groupSync', this.ongroup); - this.timeoutMillis = timeoutMillis || 60000; } @@ -72,7 +65,6 @@ class SyncRequestInner extends EventTarget { ), singleProtoJobQueue.add(MessageSender.getRequestBlockSyncMessage()), singleProtoJobQueue.add(MessageSender.getRequestContactSyncMessage()), - singleProtoJobQueue.add(MessageSender.getRequestGroupSyncMessage()), ]); } catch (error: unknown) { log.error( @@ -89,20 +81,15 @@ class SyncRequestInner extends EventTarget { this.update(); } - onGroupSyncComplete() { - this.groupSync = true; - this.update(); - } - update() { - if (this.contactSync && this.groupSync) { + if (this.contactSync) { this.dispatchEvent(new Event('success')); this.cleanup(); } } onTimeout() { - if (this.contactSync || this.groupSync) { + if (this.contactSync) { this.dispatchEvent(new Event('success')); } else { this.dispatchEvent(new Event('timeout')); @@ -113,7 +100,6 @@ class SyncRequestInner extends EventTarget { cleanup() { clearTimeout(this.timeout); this.receiver.removeEventListener('contactsync', this.oncontact); - this.receiver.removeEventListener('groupSync', this.ongroup); delete this.listeners; } } diff --git a/ts/textsecure/index.ts b/ts/textsecure/index.ts index 5ab74fe6966d..a625ca5910e7 100644 --- a/ts/textsecure/index.ts +++ b/ts/textsecure/index.ts @@ -5,7 +5,7 @@ import EventTarget from './EventTarget'; import AccountManager from './AccountManager'; import MessageReceiver from './MessageReceiver'; import utils from './Helpers'; -import { ContactBuffer, GroupBuffer } from './ContactsParser'; +import { ContactBuffer } from './ContactsParser'; import SyncRequest from './SyncRequest'; import MessageSender from './SendMessage'; import { Storage } from './Storage'; @@ -19,7 +19,6 @@ export type TextSecureType = { AccountManager: typeof AccountManager; ContactBuffer: typeof ContactBuffer; EventTarget: typeof EventTarget; - GroupBuffer: typeof GroupBuffer; MessageReceiver: typeof MessageReceiver; MessageSender: typeof MessageSender; SyncRequest: typeof SyncRequest; @@ -37,7 +36,6 @@ export const textsecure: TextSecureType = { AccountManager, ContactBuffer, EventTarget, - GroupBuffer, MessageReceiver, MessageSender, SyncRequest, diff --git a/ts/textsecure/messageReceiverEvents.ts b/ts/textsecure/messageReceiverEvents.ts index 2f7c1d256865..0019b5557e9f 100644 --- a/ts/textsecure/messageReceiverEvents.ts +++ b/ts/textsecure/messageReceiverEvents.ts @@ -11,10 +11,7 @@ import type { ProcessedDataMessage, ProcessedSent, } from './Types.d'; -import type { - ModifiedContactDetails, - ModifiedGroupDetails, -} from './ContactsParser'; +import type { ModifiedContactDetails } from './ContactsParser'; export class EmptyEvent extends Event { constructor() { @@ -84,26 +81,6 @@ export class ContactSyncEvent extends Event { } } -export type GroupEventData = Omit & - Readonly<{ - id: string; - }>; - -export class GroupEvent extends Event { - constructor( - public readonly groupDetails: GroupEventData, - public readonly receivedAtCounter: number - ) { - super('group'); - } -} - -export class GroupSyncEvent extends Event { - constructor() { - super('groupSync'); - } -} - // Emitted right before we do full decrypt on a message, but after Sealed Sender unseal export class EnvelopeUnsealedEvent extends Event { constructor(public readonly envelope: ProcessedEnvelope) { diff --git a/ts/util/handleMessageSend.ts b/ts/util/handleMessageSend.ts index aea5920c6067..c213db17008e 100644 --- a/ts/util/handleMessageSend.ts +++ b/ts/util/handleMessageSend.ts @@ -43,7 +43,6 @@ export const sendTypesEnum = z.enum([ 'blockSyncRequest', 'configurationSyncRequest', 'contactSyncRequest', // urgent because it blocks the link process - 'groupSyncRequest', 'keySyncRequest', // urgent because it blocks the link process 'pniIdentitySyncRequest', // urgent because we need our PNI to be fully functional @@ -51,7 +50,6 @@ export const sendTypesEnum = z.enum([ 'blockSync', 'configurationSync', 'contactSync', - 'groupSync', 'keySync', 'pniIdentitySync', diff --git a/yarn.lock b/yarn.lock index 8d1253522ed6..b59df11731ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2284,10 +2284,10 @@ node-gyp-build "^4.2.3" uuid "^8.3.0" -"@signalapp/mock-server@3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-3.2.1.tgz#36fd3e72b44dbcb6c82fdb27bbf56f8393e8f065" - integrity sha512-irT4U3e8Lve9HODGIlXx+vElONaPNe7Ks9QRoSPSR4o0DbUX/g+zUxfEu7gjEi0CQI8OKxcJHe7e5DbE8mvXng== +"@signalapp/mock-server@3.2.3": + version "3.2.3" + resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-3.2.3.tgz#86cebfb1d5e8054f88acc0dcb05c7e18a600dea1" + integrity sha512-JTrrYbYZLyTva/9RPsP18QXYge2Rog9uw7vCEADmTpZbJ4jO3HRw6+viuCKiExpa+Kooe1LgbFfyH3n5iTxRQg== dependencies: "@signalapp/libsignal-client" "^0.29.0" debug "^4.3.2"