Remove isPrivate, isMe, isGroupV1, isGroupV2 from model

This commit is contained in:
Josh Perez 2021-06-07 12:39:13 -04:00 committed by GitHub
parent eaf4036fc8
commit d4875fd8f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 377 additions and 267 deletions

View file

@ -13,6 +13,10 @@ import { SendOptionsType, CallbackResultType } from './textsecure/SendMessage';
import { ConversationModel } from './models/conversations'; import { ConversationModel } from './models/conversations';
import { maybeDeriveGroupV2Id } from './groups'; import { maybeDeriveGroupV2Id } from './groups';
import { assert } from './util/assert'; import { assert } from './util/assert';
import { isGroupV1, isGroupV2 } from './util/whatTypeOfConversation';
import { deprecated } from './util/deprecated';
import { getSendOptions } from './util/getSendOptions';
import { handleMessageSend } from './util/handleMessageSend';
const MAX_MESSAGE_BODY_LENGTH = 64 * 1024; const MAX_MESSAGE_BODY_LENGTH = 64 * 1024;
@ -237,7 +241,7 @@ export class ConversationController {
} }
try { try {
if (conversation.isGroupV1()) { if (isGroupV1(conversation.attributes)) {
await maybeDeriveGroupV2Id(conversation); await maybeDeriveGroupV2Id(conversation);
} }
await saveConversation(conversation.attributes); await saveConversation(conversation.attributes);
@ -556,7 +560,7 @@ export class ConversationController {
} }
let groupV2Id: undefined | string; let groupV2Id: undefined | string;
if (conversation.isGroupV1()) { if (isGroupV1(conversation.attributes)) {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await maybeDeriveGroupV2Id(conversation); await maybeDeriveGroupV2Id(conversation);
groupV2Id = conversation.get('derivedGroupV2Id'); groupV2Id = conversation.get('derivedGroupV2Id');
@ -564,7 +568,7 @@ export class ConversationController {
groupV2Id, groupV2Id,
'checkForConflicts: expected the group V2 ID to have been derived, but it was falsy' 'checkForConflicts: expected the group V2 ID to have been derived, but it was falsy'
); );
} else if (conversation.isGroupV2()) { } else if (isGroupV2(conversation.attributes)) {
groupV2Id = conversation.get('groupId'); groupV2Id = conversation.get('groupId');
} }
@ -573,7 +577,7 @@ export class ConversationController {
if (!existing) { if (!existing) {
byGroupV2Id[groupV2Id] = conversation; byGroupV2Id[groupV2Id] = conversation;
} else { } else {
const logParenthetical = conversation.isGroupV1() const logParenthetical = isGroupV1(conversation.attributes)
? ' (derived from a GV1 group ID)' ? ' (derived from a GV1 group ID)'
: ''; : '';
window.log.warn( window.log.warn(
@ -581,7 +585,10 @@ export class ConversationController {
); );
// Prefer the GV2 group. // Prefer the GV2 group.
if (conversation.isGroupV2() && !existing.isGroupV2()) { if (
isGroupV2(conversation.attributes) &&
!isGroupV2(existing.attributes)
) {
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await this.combineConversations(conversation, existing); await this.combineConversations(conversation, existing);
byGroupV2Id[groupV2Id] = conversation; byGroupV2Id[groupV2Id] = conversation;
@ -730,16 +737,14 @@ export class ConversationController {
) => Promise<CallbackResultType | void | null>; ) => Promise<CallbackResultType | void | null>;
sendOptions: SendOptionsType | undefined; sendOptions: SendOptionsType | undefined;
}> { }> {
deprecated('prepareForSend');
// id is any valid conversation identifier // id is any valid conversation identifier
const conversation = this.get(id); const conversation = this.get(id);
const sendOptions = conversation const sendOptions = conversation
? await conversation.getSendOptions(options) ? await getSendOptions(conversation.attributes, options)
: undefined; : undefined;
const wrap = conversation
? conversation.wrapSend.bind(conversation)
: async (promise: Promise<CallbackResultType | void | null>) => promise;
return { wrap, sendOptions }; return { wrap: handleMessageSend, sendOptions };
} }
async getAllGroupsInvolvingId( async getAllGroupsInvolvingId(

View file

@ -43,6 +43,7 @@ import {
UnprocessedType, UnprocessedType,
UnprocessedUpdateType, UnprocessedUpdateType,
} from './textsecure/Types.d'; } from './textsecure/Types.d';
import { getSendOptions } from './util/getSendOptions';
const TIMESTAMP_THRESHOLD = 5 * 1000; // 5 seconds const TIMESTAMP_THRESHOLD = 5 * 1000; // 5 seconds
@ -1230,7 +1231,7 @@ export class SignalProtocolStore extends EventsMixin {
await this.archiveSession(id); await this.archiveSession(id);
// Send a null message with newly-created session // Send a null message with newly-created session
const sendOptions = await conversation.getSendOptions(); const sendOptions = await getSendOptions(conversation.attributes);
await window.textsecure.messaging.sendNullMessage({ uuid }, sendOptions); await window.textsecure.messaging.sendNullMessage({ uuid }, sendOptions);
} catch (error) { } catch (error) {
// If we failed to do the session reset, then we'll allow another attempt sooner // If we failed to do the session reset, then we'll allow another attempt sooner

View file

@ -35,6 +35,8 @@ import {
} from './textsecure/MessageReceiver'; } from './textsecure/MessageReceiver';
import { connectToServerWithStoredCredentials } from './util/connectToServerWithStoredCredentials'; import { connectToServerWithStoredCredentials } from './util/connectToServerWithStoredCredentials';
import * as universalExpireTimer from './util/universalExpireTimer'; import * as universalExpireTimer from './util/universalExpireTimer';
import { isDirectConversation, isGroupV2 } from './util/whatTypeOfConversation';
import { getSendOptions } from './util/getSendOptions';
const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000; const MAX_ATTACHMENT_DOWNLOAD_AGE = 3600 * 72 * 1000;
@ -1955,7 +1957,7 @@ export async function startApp(): Promise<void> {
.getConversations() .getConversations()
.filter(c => .filter(c =>
Boolean( Boolean(
c.isPrivate() && isDirectConversation(c.attributes) &&
c.get('e164') && c.get('e164') &&
!c.get('uuid') && !c.get('uuid') &&
!c.isEverUnregistered() !c.isEverUnregistered()
@ -2502,7 +2504,10 @@ export async function startApp(): Promise<void> {
} }
// We drop typing notifications in groups we're not a part of // We drop typing notifications in groups we're not a part of
if (!conversation.isPrivate() && !conversation.hasMember(ourId)) { if (
!isDirectConversation(conversation.attributes) &&
!conversation.hasMember(ourId)
) {
window.log.warn( window.log.warn(
`Received typing indicator for group ${conversation.idForLogging()}, which we're not a part of. Dropping.` `Received typing indicator for group ${conversation.idForLogging()}, which we're not a part of. Dropping.`
); );
@ -2703,7 +2708,7 @@ export async function startApp(): Promise<void> {
id, id,
'group' 'group'
); );
if (conversation.isGroupV2()) { if (isGroupV2(conversation.attributes)) {
window.log.warn( window.log.warn(
'Got group sync for v2 group: ', 'Got group sync for v2 group: ',
conversation.idForLogging() conversation.idForLogging()
@ -3578,7 +3583,7 @@ export async function startApp(): Promise<void> {
); );
const plaintext = PlaintextContent.from(message); const plaintext = PlaintextContent.from(message);
const options = await conversation.getSendOptions(); const options = await getSendOptions(conversation.attributes);
const result = await window.textsecure.messaging.sendRetryRequest({ const result = await window.textsecure.messaging.sendRetryRequest({
plaintext, plaintext,
options, options,

View file

@ -79,6 +79,13 @@ import { CURRENT_SCHEMA_VERSION as MAX_MESSAGE_SCHEMA } from '../js/modules/type
import { ConversationModel } from './models/conversations'; import { ConversationModel } from './models/conversations';
import { getGroupSizeHardLimit } from './groups/limits'; import { getGroupSizeHardLimit } from './groups/limits';
import { ourProfileKeyService } from './services/ourProfileKey'; import { ourProfileKeyService } from './services/ourProfileKey';
import {
isGroupV1 as getIsGroupV1,
isGroupV2 as getIsGroupV2,
isMe,
} from './util/whatTypeOfConversation';
import { handleMessageSend } from './util/handleMessageSend';
import { getSendOptions } from './util/getSendOptions';
export { joinViaLink } from './groups/joinViaLink'; export { joinViaLink } from './groups/joinViaLink';
@ -1231,7 +1238,7 @@ export async function modifyGroupV2({
}): Promise<void> { }): Promise<void> {
const idLog = `${name}/${conversation.idForLogging()}`; const idLog = `${name}/${conversation.idForLogging()}`;
if (!conversation.isGroupV2()) { if (!getIsGroupV2(conversation.attributes)) {
throw new Error( throw new Error(
`modifyGroupV2/${idLog}: Called for non-GroupV2 conversation` `modifyGroupV2/${idLog}: Called for non-GroupV2 conversation`
); );
@ -1297,13 +1304,13 @@ export async function modifyGroupV2({
? await ourProfileKeyService.get() ? await ourProfileKeyService.get()
: undefined; : undefined;
const sendOptions = await conversation.getSendOptions(); const sendOptions = await getSendOptions(conversation.attributes);
const timestamp = Date.now(); const timestamp = Date.now();
const { const {
ContentHint, ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message; } = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const promise = conversation.wrapSend( const promise = handleMessageSend(
window.Signal.Util.sendToGroup( window.Signal.Util.sendToGroup(
{ {
groupV2: conversation.getGroupV2Info({ groupV2: conversation.getGroupV2Info({
@ -1676,7 +1683,7 @@ export async function createGroupV2({
const { const {
ContentHint, ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message; } = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const sendOptions = await conversation.getSendOptions(); const sendOptions = await getSendOptions(conversation.attributes);
await wrapWithSyncMessageSend({ await wrapWithSyncMessageSend({
conversation, conversation,
@ -1729,7 +1736,7 @@ export async function hasV1GroupBeenMigrated(
conversation: ConversationModel conversation: ConversationModel
): Promise<boolean> { ): Promise<boolean> {
const logId = conversation.idForLogging(); const logId = conversation.idForLogging();
const isGroupV1 = conversation.isGroupV1(); const isGroupV1 = getIsGroupV1(conversation.attributes);
if (!isGroupV1) { if (!isGroupV1) {
window.log.warn( window.log.warn(
`checkForGV2Existence/${logId}: Called for non-GroupV1 conversation!` `checkForGV2Existence/${logId}: Called for non-GroupV1 conversation!`
@ -1766,7 +1773,7 @@ export async function hasV1GroupBeenMigrated(
export async function maybeDeriveGroupV2Id( export async function maybeDeriveGroupV2Id(
conversation: ConversationModel conversation: ConversationModel
): Promise<boolean> { ): Promise<boolean> {
const isGroupV1 = conversation.isGroupV1(); const isGroupV1 = getIsGroupV1(conversation.attributes);
const groupV1Id = conversation.get('groupId'); const groupV1Id = conversation.get('groupId');
const derived = conversation.get('derivedGroupV2Id'); const derived = conversation.get('derivedGroupV2Id');
@ -1797,7 +1804,7 @@ type MigratePropsType = {
export async function isGroupEligibleToMigrate( export async function isGroupEligibleToMigrate(
conversation: ConversationModel conversation: ConversationModel
): Promise<boolean> { ): Promise<boolean> {
if (!conversation.isGroupV1()) { if (!getIsGroupV1(conversation.attributes)) {
return false; return false;
} }
@ -1860,7 +1867,7 @@ export async function getGroupMigrationMembers(
`getGroupMigrationMembers/${logId}: membersV2 - missing local contact for ${e164}, skipping.` `getGroupMigrationMembers/${logId}: membersV2 - missing local contact for ${e164}, skipping.`
); );
} }
if (!contact.isMe() && window.GV2_MIGRATION_DISABLE_ADD) { if (!isMe(contact.attributes) && window.GV2_MIGRATION_DISABLE_ADD) {
window.log.warn( window.log.warn(
`getGroupMigrationMembers/${logId}: membersV2 - skipping ${e164} due to GV2_MIGRATION_DISABLE_ADD flag` `getGroupMigrationMembers/${logId}: membersV2 - skipping ${e164} due to GV2_MIGRATION_DISABLE_ADD flag`
); );
@ -1947,7 +1954,7 @@ export async function getGroupMigrationMembers(
return null; return null;
} }
if (!contact.isMe() && window.GV2_MIGRATION_DISABLE_INVITE) { if (!isMe(contact.attributes) && window.GV2_MIGRATION_DISABLE_INVITE) {
window.log.warn( window.log.warn(
`getGroupMigrationMembers/${logId}: pendingMembersV2 - skipping ${e164} due to GV2_MIGRATION_DISABLE_INVITE flag` `getGroupMigrationMembers/${logId}: pendingMembersV2 - skipping ${e164} due to GV2_MIGRATION_DISABLE_INVITE flag`
); );
@ -2173,7 +2180,7 @@ export async function initiateMigrationToGroupV2(
}); });
} catch (error) { } catch (error) {
const logId = conversation.idForLogging(); const logId = conversation.idForLogging();
if (!conversation.isGroupV1()) { if (!getIsGroupV1(conversation.attributes)) {
throw error; throw error;
} }
@ -2203,7 +2210,7 @@ export async function initiateMigrationToGroupV2(
const { const {
ContentHint, ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message; } = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
const sendOptions = await conversation.getSendOptions(); const sendOptions = await getSendOptions(conversation.attributes);
await wrapWithSyncMessageSend({ await wrapWithSyncMessageSend({
conversation, conversation,
@ -2361,7 +2368,7 @@ export async function joinGroupV2ViaLinkAndMigrate({
inviteLinkPassword: string; inviteLinkPassword: string;
revision: number; revision: number;
}): Promise<void> { }): Promise<void> {
const isGroupV1 = conversation.isGroupV1(); const isGroupV1 = getIsGroupV1(conversation.attributes);
const previousGroupV1Id = conversation.get('groupId'); const previousGroupV1Id = conversation.get('groupId');
if (!isGroupV1 || !previousGroupV1Id) { if (!isGroupV1 || !previousGroupV1Id) {
@ -2451,7 +2458,7 @@ export async function respondToGroupV2Migration({
// Ensure we have the credentials we need before attempting GroupsV2 operations // Ensure we have the credentials we need before attempting GroupsV2 operations
await maybeFetchNewCredentials(); await maybeFetchNewCredentials();
const isGroupV1 = conversation.isGroupV1(); const isGroupV1 = getIsGroupV1(conversation.attributes);
const previousGroupV1Id = conversation.get('groupId'); const previousGroupV1Id = conversation.get('groupId');
if (!isGroupV1 || !previousGroupV1Id) { if (!isGroupV1 || !previousGroupV1Id) {

View file

@ -13,6 +13,7 @@ import {
} from '../groups'; } from '../groups';
import { arrayBufferToBase64, base64ToArrayBuffer } from '../Crypto'; import { arrayBufferToBase64, base64ToArrayBuffer } from '../Crypto';
import { longRunningTaskWrapper } from '../util/longRunningTaskWrapper'; import { longRunningTaskWrapper } from '../util/longRunningTaskWrapper';
import { isGroupV1 } from '../util/whatTypeOfConversation';
import type { GroupJoinInfoClass } from '../textsecure.d'; import type { GroupJoinInfoClass } from '../textsecure.d';
import type { ConversationAttributesType } from '../model-types.d'; import type { ConversationAttributesType } from '../model-types.d';
@ -285,7 +286,7 @@ export async function joinViaLink(hash: string): Promise<void> {
); );
} }
if (targetConversation.isGroupV1()) { if (isGroupV1(targetConversation.attributes)) {
await targetConversation.joinGroupV2ViaLinkAndMigrate({ await targetConversation.joinGroupV2ViaLinkAndMigrate({
approvalRequired, approvalRequired,
inviteLinkPassword, inviteLinkPassword,

File diff suppressed because it is too large Load diff

View file

@ -57,6 +57,14 @@ import { MIMEType } from '../types/MIME';
import { LinkPreviewType } from '../types/message/LinkPreviews'; import { LinkPreviewType } from '../types/message/LinkPreviews';
import { ourProfileKeyService } from '../services/ourProfileKey'; import { ourProfileKeyService } from '../services/ourProfileKey';
import { markRead, setToExpire } from '../services/MessageUpdater'; import { markRead, setToExpire } from '../services/MessageUpdater';
import {
isDirectConversation,
isGroupV1,
isGroupV2,
isMe,
} from '../util/whatTypeOfConversation';
import { handleMessageSend } from '../util/handleMessageSend';
import { getSendOptions } from '../util/getSendOptions';
/* eslint-disable camelcase */ /* eslint-disable camelcase */
/* eslint-disable more/no-then */ /* eslint-disable more/no-then */
@ -744,7 +752,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
getPropsForSafetyNumberNotification(): SafetyNumberNotificationProps { getPropsForSafetyNumberNotification(): SafetyNumberNotificationProps {
const conversation = this.getConversation(); const conversation = this.getConversation();
const isGroup = Boolean(conversation && !conversation.isPrivate()); const isGroup = Boolean(
conversation && !isDirectConversation(conversation.attributes)
);
const identifier = this.get('key_changed'); const identifier = this.get('key_changed');
const contact = this.findAndFormatContact(identifier); const contact = this.findAndFormatContact(identifier);
@ -981,7 +991,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
: undefined; : undefined;
const conversation = this.getConversation(); const conversation = this.getConversation();
const isGroup = conversation && !conversation.isPrivate(); const isGroup =
conversation && !isDirectConversation(conversation.attributes);
const sticker = this.get('sticker'); const sticker = this.get('sticker');
const isTapToView = this.isTapToView(); const isTapToView = this.isTapToView();
@ -1316,7 +1327,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
let authorTitle: string; let authorTitle: string;
let isFromMe: boolean; let isFromMe: boolean;
if (contact && contact.isPrivate()) { if (contact && isDirectConversation(contact.attributes)) {
const contactPhoneNumber = contact.get('e164'); const contactPhoneNumber = contact.get('e164');
authorId = contact.id; authorId = contact.id;
@ -1328,7 +1339,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
: undefined; : undefined;
authorProfileName = contact.getProfileName(); authorProfileName = contact.getProfileName();
authorTitle = contact.getTitle(); authorTitle = contact.getTitle();
isFromMe = contact.isMe(); isFromMe = isMe(contact.attributes);
} else { } else {
window.log.warn( window.log.warn(
'getPropsForQuote: contact was missing. This may indicate a bookkeeping error or bad data from another client. Returning a placeholder contact.' 'getPropsForQuote: contact was missing. This may indicate a bookkeeping error or bad data from another client. Returning a placeholder contact.'
@ -1529,7 +1540,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
return { text: '' }; return { text: '' };
} }
if (fromContact.isMe()) { if (isMe(fromContact.attributes)) {
messages.push(window.i18n('youUpdatedTheGroup')); messages.push(window.i18n('youUpdatedTheGroup'));
} else { } else {
messages.push(window.i18n('updatedTheGroup', [fromContact.getTitle()])); messages.push(window.i18n('updatedTheGroup', [fromContact.getTitle()]));
@ -1540,7 +1551,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
window.ConversationController.getOrCreate(item, 'private') window.ConversationController.getOrCreate(item, 'private')
); );
const joinedWithoutMe = joinedContacts.filter( const joinedWithoutMe = joinedContacts.filter(
contact => !contact.isMe() contact => !isMe(contact.attributes)
); );
if (joinedContacts.length > 1) { if (joinedContacts.length > 1) {
@ -1558,7 +1569,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
groupUpdate.joined[0], groupUpdate.joined[0],
'private' 'private'
); );
if (joinedContact.isMe()) { if (isMe(joinedContact.attributes)) {
messages.push(window.i18n('youJoinedTheGroup')); messages.push(window.i18n('youJoinedTheGroup'));
} else { } else {
messages.push( messages.push(
@ -2282,13 +2293,13 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
let promise; let promise;
const options = await conversation.getSendOptions(); const options = await getSendOptions(conversation.attributes);
const { const {
ContentHint, ContentHint,
} = window.textsecure.protobuf.UnidentifiedSenderMessage.Message; } = window.textsecure.protobuf.UnidentifiedSenderMessage.Message;
if (conversation.isPrivate()) { if (isDirectConversation(conversation.attributes)) {
const [identifier] = recipients; const [identifier] = recipients;
promise = window.textsecure.messaging.sendMessageToIdentifier( promise = window.textsecure.messaging.sendMessageToIdentifier(
identifier, identifier,
@ -2351,7 +2362,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
); );
} }
return this.send(conversation.wrapSend(promise)); return this.send(handleMessageSend(promise));
} }
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line class-methods-use-this
@ -2534,7 +2545,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
sendOptions, sendOptions,
} = await window.ConversationController.prepareForSend(identifier); } = await window.ConversationController.prepareForSend(identifier);
const group = const group =
groupId && parentConversation?.isGroupV1() groupId && isGroupV1(parentConversation?.attributes)
? { ? {
id: groupId, id: groupId,
type: window.textsecure.protobuf.GroupContext.Type.DELIVER, type: window.textsecure.protobuf.GroupContext.Type.DELIVER,
@ -2576,7 +2587,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
[identifier], [identifier],
contentMessage, contentMessage,
ContentHint.RESENDABLE, ContentHint.RESENDABLE,
groupId && parentConversation?.isGroupV2() ? groupId : undefined, groupId && isGroupV2(parentConversation?.attributes)
? groupId
: undefined,
sendOptions sendOptions
); );
@ -2767,7 +2780,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
retryOptions: options, retryOptions: options,
}); });
const sendOptions = await conv.getSendOptions(); const sendOptions = await getSendOptions(conv.attributes);
// We don't have to check `sent_to` here, because: // We don't have to check `sent_to` here, because:
// //
@ -2776,11 +2789,11 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
// in a single request to the server. So partial success is not // in a single request to the server. So partial success is not
// possible. // possible.
await this.send( await this.send(
conv.wrapSend( handleMessageSend(
// TODO: DESKTOP-724 // TODO: DESKTOP-724
// resetSession returns `Array<void>` which is incompatible with the // resetSession returns `Array<void>` which is incompatible with the
// expected promise return values. `[]` is truthy and wrapSend assumes // expected promise return values. `[]` is truthy and handleMessageSend
// it's a valid callback result type // assumes it's a valid callback result type
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore // @ts-ignore
window.textsecure.messaging.resetSession( window.textsecure.messaging.resetSession(
@ -3601,7 +3614,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
// GroupV2 // GroupV2
if (initialMessage.groupV2) { if (initialMessage.groupV2) {
if (conversation.isGroupV1()) { if (isGroupV1(conversation.attributes)) {
// If we received a GroupV2 message in a GroupV1 group, we migrate! // If we received a GroupV2 message in a GroupV1 group, we migrate!
const { revision, groupChange } = initialMessage.groupV2; const { revision, groupChange } = initialMessage.groupV2;
@ -3660,7 +3673,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
e164: source, e164: source,
uuid: sourceUuid, uuid: sourceUuid,
})!; })!;
const isGroupV2 = Boolean(initialMessage.groupV2); const hasGroupV2Prop = Boolean(initialMessage.groupV2);
const isV1GroupUpdate = const isV1GroupUpdate =
initialMessage.group && initialMessage.group &&
initialMessage.group.type !== initialMessage.group.type !==
@ -3670,8 +3683,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
// after applying the message's associated group changes. // after applying the message's associated group changes.
if ( if (
type === 'incoming' && type === 'incoming' &&
!conversation.isPrivate() && !isDirectConversation(conversation.attributes) &&
isGroupV2 && hasGroupV2Prop &&
(conversation.get('left') || (conversation.get('left') ||
!conversation.hasMember(ourConversationId) || !conversation.hasMember(ourConversationId) ||
!conversation.hasMember(senderId)) !conversation.hasMember(senderId))
@ -3690,8 +3703,8 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
// messages. We detect that via a missing 'members' field. // messages. We detect that via a missing 'members' field.
if ( if (
type === 'incoming' && type === 'incoming' &&
!conversation.isPrivate() && !isDirectConversation(conversation.attributes) &&
!isGroupV2 && !hasGroupV2Prop &&
!isV1GroupUpdate && !isV1GroupUpdate &&
conversation.get('members') && conversation.get('members') &&
(conversation.get('left') || !conversation.hasMember(ourConversationId)) (conversation.get('left') || !conversation.hasMember(ourConversationId))
@ -3705,7 +3718,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
// Because GroupV1 messages can now be multiplexed into GroupV2 conversations, we // Because GroupV1 messages can now be multiplexed into GroupV2 conversations, we
// drop GroupV1 updates in GroupV2 groups. // drop GroupV1 updates in GroupV2 groups.
if (isV1GroupUpdate && conversation.isGroupV2()) { if (isV1GroupUpdate && isGroupV2(conversation.attributes)) {
window.log.warn( window.log.warn(
`Received GroupV1 update in GroupV2 conversation ${conversation.idForLogging()}. Dropping.` `Received GroupV1 update in GroupV2 conversation ${conversation.idForLogging()}. Dropping.`
); );
@ -3793,7 +3806,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
}; };
// GroupV1 // GroupV1
if (!isGroupV2 && dataMessage.group) { if (!hasGroupV2Prop && dataMessage.group) {
const pendingGroupUpdate = []; const pendingGroupUpdate = [];
const memberConversations: Array<ConversationModel> = await Promise.all( const memberConversations: Array<ConversationModel> = await Promise.all(
dataMessage.group.membersE164.map((e164: string) => dataMessage.group.membersE164.map((e164: string) =>
@ -3920,7 +3933,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
return; return;
} }
if (sender.isMe()) { if (isMe(sender.attributes)) {
attributes.left = true; attributes.left = true;
pendingGroupUpdate.push(['left', 'You']); pendingGroupUpdate.push(['left', 'You']);
} else { } else {
@ -3962,7 +3975,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
message.set({ expireTimer: dataMessage.expireTimer }); message.set({ expireTimer: dataMessage.expireTimer });
} }
if (!isGroupV2) { if (!hasGroupV2Prop) {
if (message.isExpirationTimerUpdate()) { if (message.isExpirationTimerUpdate()) {
message.set({ message.set({
expirationTimerUpdate: { expirationTimerUpdate: {
@ -4023,7 +4036,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
sourceUuid === window.textsecure.storage.user.getUuid() sourceUuid === window.textsecure.storage.user.getUuid()
) { ) {
conversation.set({ profileSharing: true }); conversation.set({ profileSharing: true });
} else if (conversation.isPrivate()) { } else if (isDirectConversation(conversation.attributes)) {
conversation.setProfileKey(profileKey); conversation.setProfileKey(profileKey);
} else { } else {
const localId = window.ConversationController.ensureContactIds({ const localId = window.ConversationController.ensureContactIds({
@ -4214,7 +4227,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
} }
// A sync'd message to ourself is automatically considered read/delivered // A sync'd message to ourself is automatically considered read/delivered
if (isFirstRun && conversation.isMe()) { if (isFirstRun && isMe(conversation.attributes)) {
message.set({ message.set({
read_by: conversation.getRecipients(), read_by: conversation.getRecipients(),
delivered_to: conversation.getRecipients(), delivered_to: conversation.getRecipients(),

View file

@ -69,6 +69,7 @@ import {
REQUESTED_VIDEO_FRAMERATE, REQUESTED_VIDEO_FRAMERATE,
} from '../calling/constants'; } from '../calling/constants';
import { notify } from './notify'; import { notify } from './notify';
import { getSendOptions } from '../util/getSendOptions';
const RINGRTC_HTTP_METHOD_TO_OUR_HTTP_METHOD: Map< const RINGRTC_HTTP_METHOD_TO_OUR_HTTP_METHOD: Map<
HttpMethod, HttpMethod,
@ -783,7 +784,7 @@ export class CallingClass {
} }
const groupV2 = conversation.getGroupV2Info(); const groupV2 = conversation.getGroupV2Info();
const sendOptions = await conversation.getSendOptions(); const sendOptions = await getSendOptions(conversation.attributes);
if (!groupV2) { if (!groupV2) {
window.log.error( window.log.error(
'Unable to send group call update message for conversation that lacks groupV2 info' 'Unable to send group call update message for conversation that lacks groupV2 info'
@ -1408,7 +1409,7 @@ export class CallingClass {
): Promise<boolean> { ): Promise<boolean> {
const conversation = window.ConversationController.get(remoteUserId); const conversation = window.ConversationController.get(remoteUserId);
const sendOptions = conversation const sendOptions = conversation
? await conversation.getSendOptions() ? await getSendOptions(conversation.attributes)
: undefined; : undefined;
if (!window.textsecure.messaging) { if (!window.textsecure.messaging) {

View file

@ -35,6 +35,10 @@ import { sleep } from '../util/sleep';
import { isMoreRecentThan } from '../util/timestamp'; import { isMoreRecentThan } from '../util/timestamp';
import { isStorageWriteFeatureEnabled } from '../storage/isFeatureEnabled'; import { isStorageWriteFeatureEnabled } from '../storage/isFeatureEnabled';
import { ourProfileKeyService } from './ourProfileKey'; import { ourProfileKeyService } from './ourProfileKey';
import {
ConversationTypes,
typeofConversation,
} from '../util/whatTypeOfConversation';
const { const {
eraseStorageServiceStateFromConversations, eraseStorageServiceStateFromConversations,
@ -152,22 +156,24 @@ async function generateManifest(
const identifier = new window.textsecure.protobuf.ManifestRecord.Identifier(); const identifier = new window.textsecure.protobuf.ManifestRecord.Identifier();
let storageRecord; let storageRecord;
if (conversation.isMe()) {
const conversationType = typeofConversation(conversation.attributes);
if (conversationType === ConversationTypes.Me) {
storageRecord = new window.textsecure.protobuf.StorageRecord(); storageRecord = new window.textsecure.protobuf.StorageRecord();
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
storageRecord.account = await toAccountRecord(conversation); storageRecord.account = await toAccountRecord(conversation);
identifier.type = ITEM_TYPE.ACCOUNT; identifier.type = ITEM_TYPE.ACCOUNT;
} else if (conversation.isPrivate()) { } else if (conversationType === ConversationTypes.Direct) {
storageRecord = new window.textsecure.protobuf.StorageRecord(); storageRecord = new window.textsecure.protobuf.StorageRecord();
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
storageRecord.contact = await toContactRecord(conversation); storageRecord.contact = await toContactRecord(conversation);
identifier.type = ITEM_TYPE.CONTACT; identifier.type = ITEM_TYPE.CONTACT;
} else if (conversation.isGroupV2()) { } else if (conversationType === ConversationTypes.GroupV2) {
storageRecord = new window.textsecure.protobuf.StorageRecord(); storageRecord = new window.textsecure.protobuf.StorageRecord();
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
storageRecord.groupV2 = await toGroupV2Record(conversation); storageRecord.groupV2 = await toGroupV2Record(conversation);
identifier.type = ITEM_TYPE.GROUPV2; identifier.type = ITEM_TYPE.GROUPV2;
} else if (conversation.isGroupV1()) { } else if (conversationType === ConversationTypes.GroupV1) {
storageRecord = new window.textsecure.protobuf.StorageRecord(); storageRecord = new window.textsecure.protobuf.StorageRecord();
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
storageRecord.groupV1 = await toGroupV1Record(conversation); storageRecord.groupV1 = await toGroupV1Record(conversation);

View file

@ -43,6 +43,7 @@ import {
set as setUniversalExpireTimer, set as setUniversalExpireTimer,
} from '../util/universalExpireTimer'; } from '../util/universalExpireTimer';
import { ourProfileKeyService } from './ourProfileKey'; import { ourProfileKeyService } from './ourProfileKey';
import { isGroupV1, isGroupV2 } from '../util/whatTypeOfConversation';
const { updateConversation } = dataInterface; const { updateConversation } = dataInterface;
@ -241,7 +242,7 @@ export async function toAccountRecord(
uuid: pinnedConversation.get('uuid'), uuid: pinnedConversation.get('uuid'),
e164: pinnedConversation.get('e164'), e164: pinnedConversation.get('e164'),
}; };
} else if (pinnedConversation.isGroupV1()) { } else if (isGroupV1(pinnedConversation.attributes)) {
pinnedConversationRecord.identifier = 'legacyGroupId'; pinnedConversationRecord.identifier = 'legacyGroupId';
const groupId = pinnedConversation.get('groupId'); const groupId = pinnedConversation.get('groupId');
if (!groupId) { if (!groupId) {
@ -252,7 +253,7 @@ export async function toAccountRecord(
pinnedConversationRecord.legacyGroupId = fromEncodedBinaryToArrayBuffer( pinnedConversationRecord.legacyGroupId = fromEncodedBinaryToArrayBuffer(
groupId groupId
); );
} else if (pinnedConversation.isGroupV2()) { } else if (isGroupV2(pinnedConversation.attributes)) {
pinnedConversationRecord.identifier = 'groupMasterKey'; pinnedConversationRecord.identifier = 'groupMasterKey';
const masterKey = pinnedConversation.get('masterKey'); const masterKey = pinnedConversation.get('masterKey');
if (!masterKey) { if (!masterKey) {
@ -508,7 +509,7 @@ export async function mergeGroupV1Record(
// where the binary representation of its ID matches a v2 record in memory. // where the binary representation of its ID matches a v2 record in memory.
// Here we ensure that the record we're about to process is GV1 otherwise // Here we ensure that the record we're about to process is GV1 otherwise
// we drop the update. // we drop the update.
if (conversation && !conversation.isGroupV1()) { if (conversation && !isGroupV1(conversation.attributes)) {
throw new Error( throw new Error(
`Record has group type mismatch ${conversation.idForLogging()}` `Record has group type mismatch ${conversation.idForLogging()}`
); );
@ -565,7 +566,7 @@ export async function mergeGroupV1Record(
let hasPendingChanges: boolean; let hasPendingChanges: boolean;
if (conversation.isGroupV1()) { if (isGroupV1(conversation.attributes)) {
addUnknownFields(groupV1Record, conversation); addUnknownFields(groupV1Record, conversation);
hasPendingChanges = doesRecordHavePendingChanges( hasPendingChanges = doesRecordHavePendingChanges(
@ -684,7 +685,7 @@ export async function mergeGroupV2Record(
const isFirstSync = !window.storage.get('storageFetchComplete'); const isFirstSync = !window.storage.get('storageFetchComplete');
const dropInitialJoinMessage = isFirstSync; const dropInitialJoinMessage = isFirstSync;
if (conversation.isGroupV1()) { if (isGroupV1(conversation.attributes)) {
// If we found a GroupV1 conversation from this incoming GroupV2 record, we need to // If we found a GroupV1 conversation from this incoming GroupV2 record, we need to
// migrate it! // migrate it!

11
ts/util/deprecated.ts Normal file
View file

@ -0,0 +1,11 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { getEnvironment, Environment } from '../environment';
import * as log from '../logging/log';
export function deprecated(message?: string): void {
if (getEnvironment() === Environment.Development) {
log.error(new Error(`This method is deprecated: ${message}`));
}
}

View file

@ -40,9 +40,10 @@ export async function getSendOptions(
if (!conversation) { if (!conversation) {
return; return;
} }
const { const { sendMetadata: conversationSendMetadata } = await getSendOptions(
sendMetadata: conversationSendMetadata, conversation.attributes,
} = await conversation.getSendOptions(options); options
);
Object.assign(sendMetadata, conversationSendMetadata || {}); Object.assign(sendMetadata, conversationSendMetadata || {});
}) })
); );

View file

@ -38,6 +38,7 @@ import {
import { ContentClass } from '../textsecure.d'; import { ContentClass } from '../textsecure.d';
import { assert } from './assert'; import { assert } from './assert';
import { isGroupV2 } from './whatTypeOfConversation';
const ERROR_EXPIRED_OR_MISSING_DEVICES = 409; const ERROR_EXPIRED_OR_MISSING_DEVICES = 409;
const ERROR_STALE_DEVICES = 410; const ERROR_STALE_DEVICES = 410;
@ -116,7 +117,7 @@ export async function sendContentMessageToGroup({
if ( if (
ourConversation?.get('capabilities')?.senderKey && ourConversation?.get('capabilities')?.senderKey &&
conversation.isGroupV2() isGroupV2(conversation.attributes)
) { ) {
try { try {
return await sendToGroupViaSenderKey({ return await sendToGroupViaSenderKey({
@ -138,7 +139,7 @@ export async function sendContentMessageToGroup({
} }
} }
const groupId = conversation.isGroupV2() const groupId = isGroupV2(conversation.attributes)
? conversation.get('groupId') ? conversation.get('groupId')
: undefined; : undefined;
return window.textsecure.messaging.sendGroupProto( return window.textsecure.messaging.sendGroupProto(
@ -191,7 +192,7 @@ export async function sendToGroupViaSenderKey(options: {
} }
const groupId = conversation.get('groupId'); const groupId = conversation.get('groupId');
if (!groupId || !conversation.isGroupV2()) { if (!groupId || !isGroupV2(conversation.attributes)) {
throw new Error( throw new Error(
`sendToGroupViaSenderKey/${logId}: Missing groupId or group is not GV2` `sendToGroupViaSenderKey/${logId}: Missing groupId or group is not GV2`
); );

View file

@ -2,11 +2,12 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { ConversationModel } from '../models/conversations'; import { ConversationModel } from '../models/conversations';
import { isMe } from './whatTypeOfConversation';
export async function shouldRespondWithProfileKey( export async function shouldRespondWithProfileKey(
sender: ConversationModel sender: ConversationModel
): Promise<boolean> { ): Promise<boolean> {
if (sender.isMe() || !sender.getAccepted() || sender.isBlocked()) { if (isMe(sender.attributes) || !sender.getAccepted() || sender.isBlocked()) {
return false; return false;
} }

View file

@ -2,6 +2,14 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { ConversationAttributesType } from '../model-types.d'; import { ConversationAttributesType } from '../model-types.d';
import { base64ToArrayBuffer, fromEncodedBinaryToArrayBuffer } from '../Crypto';
export enum ConversationTypes {
Me = 'Me',
Direct = 'Direct',
GroupV1 = 'GroupV1',
GroupV2 = 'GroupV2',
}
export function isDirectConversation( export function isDirectConversation(
conversationAttrs: ConversationAttributesType conversationAttrs: ConversationAttributesType
@ -15,3 +23,56 @@ export function isMe(conversationAttrs: ConversationAttributesType): boolean {
const ourUuid = window.textsecure.storage.user.getUuid(); const ourUuid = window.textsecure.storage.user.getUuid();
return Boolean((e164 && e164 === ourNumber) || (uuid && uuid === ourUuid)); return Boolean((e164 && e164 === ourNumber) || (uuid && uuid === ourUuid));
} }
export function isGroupV1(
conversationAttrs: ConversationAttributesType
): boolean {
const { groupId } = conversationAttrs;
if (!groupId) {
return false;
}
const buffer = fromEncodedBinaryToArrayBuffer(groupId);
return buffer.byteLength === window.Signal.Groups.ID_V1_LENGTH;
}
export function isGroupV2(
conversationAttrs: ConversationAttributesType
): boolean {
const { groupId, groupVersion = 0 } = conversationAttrs;
if (!groupId) {
return false;
}
try {
return (
groupVersion === 2 &&
base64ToArrayBuffer(groupId).byteLength === window.Signal.Groups.ID_LENGTH
);
} catch (error) {
window.log.error('isGroupV2: Failed to process groupId in base64!');
return false;
}
}
export function typeofConversation(
conversationAttrs: ConversationAttributesType
): ConversationTypes | undefined {
if (isMe(conversationAttrs)) {
return ConversationTypes.Me;
}
if (isDirectConversation(conversationAttrs)) {
return ConversationTypes.Direct;
}
if (isGroupV2(conversationAttrs)) {
return ConversationTypes.GroupV2;
}
if (isGroupV1(conversationAttrs)) {
return ConversationTypes.GroupV1;
}
return undefined;
}

View file

@ -15,6 +15,12 @@ import { maybeParseUrl } from '../util/url';
import { addReportSpamJob } from '../jobs/helpers/addReportSpamJob'; import { addReportSpamJob } from '../jobs/helpers/addReportSpamJob';
import { reportSpamJobQueue } from '../jobs/reportSpamJobQueue'; import { reportSpamJobQueue } from '../jobs/reportSpamJobQueue';
import { GroupNameCollisionsWithIdsByTitle } from '../util/groupMemberNameCollisions'; import { GroupNameCollisionsWithIdsByTitle } from '../util/groupMemberNameCollisions';
import {
isDirectConversation,
isGroupV1,
isGroupV2,
isMe,
} from '../util/whatTypeOfConversation';
type GetLinkPreviewImageResult = { type GetLinkPreviewImageResult = {
data: ArrayBuffer; data: ArrayBuffer;
@ -486,7 +492,7 @@ Whisper.ConversationView = Whisper.View.extend({
onResetSession: () => this.endSession(), onResetSession: () => this.endSession(),
onSearchInConversation: () => { onSearchInConversation: () => {
const { searchInConversation } = window.reduxActions.search; const { searchInConversation } = window.reduxActions.search;
const name = this.model.isMe() const name = isMe(this.model.attributes)
? window.i18n('noteToSelf') ? window.i18n('noteToSelf')
: this.model.getTitle(); : this.model.getTitle();
searchInConversation(this.model.id, name); searchInConversation(this.model.id, name);
@ -1281,7 +1287,7 @@ Whisper.ConversationView = Whisper.View.extend({
async startMigrationToGV2(): Promise<void> { async startMigrationToGV2(): Promise<void> {
const logId = this.model.idForLogging(); const logId = this.model.idForLogging();
if (!this.model.isGroupV1()) { if (!isGroupV1(this.model.attributes)) {
throw new Error( throw new Error(
`startMigrationToGV2/${logId}: Cannot start, not a GroupV1 group` `startMigrationToGV2/${logId}: Cannot start, not a GroupV1 group`
); );
@ -2682,7 +2688,7 @@ Whisper.ConversationView = Whisper.View.extend({
let model = providedMembers || this.model.contactCollection; let model = providedMembers || this.model.contactCollection;
if (!providedMembers && this.model.isGroupV2()) { if (!providedMembers && isGroupV2(this.model.attributes)) {
model = new Whisper.GroupConversationCollection( model = new Whisper.GroupConversationCollection(
this.model.get('membersV2').map(({ conversationId, role }: any) => ({ this.model.get('membersV2').map(({ conversationId, role }: any) => ({
conversation: window.ConversationController.get(conversationId), conversation: window.ConversationController.get(conversationId),
@ -2738,7 +2744,7 @@ Whisper.ConversationView = Whisper.View.extend({
showSafetyNumber(id: any) { showSafetyNumber(id: any) {
let conversation; let conversation;
if (!id && this.model.isPrivate()) { if (!id && isDirectConversation(this.model.attributes)) {
// eslint-disable-next-line prefer-destructuring // eslint-disable-next-line prefer-destructuring
conversation = this.model; conversation = this.model;
} else { } else {
@ -3786,19 +3792,22 @@ Whisper.ConversationView = Whisper.View.extend({
ToastView = Whisper.InvalidConversationToast; ToastView = Whisper.InvalidConversationToast;
} }
if ( if (
this.model.isPrivate() && isDirectConversation(this.model.attributes) &&
(window.storage.isBlocked(this.model.get('e164')) || (window.storage.isBlocked(this.model.get('e164')) ||
window.storage.isUuidBlocked(this.model.get('uuid'))) window.storage.isUuidBlocked(this.model.get('uuid')))
) { ) {
ToastView = Whisper.BlockedToast; ToastView = Whisper.BlockedToast;
} }
if ( if (
!this.model.isPrivate() && !isDirectConversation(this.model.attributes) &&
window.storage.isGroupBlocked(this.model.get('groupId')) window.storage.isGroupBlocked(this.model.get('groupId'))
) { ) {
ToastView = Whisper.BlockedGroupToast; ToastView = Whisper.BlockedGroupToast;
} }
if (!this.model.isPrivate() && this.model.get('left')) { if (
!isDirectConversation(this.model.attributes) &&
this.model.get('left')
) {
ToastView = Whisper.LeftGroupToast; ToastView = Whisper.LeftGroupToast;
} }
if (messageText && messageText.length > MAX_MESSAGE_BODY_LENGTH) { if (messageText && messageText.length > MAX_MESSAGE_BODY_LENGTH) {