Handle and send isRecipientUpdate sync messages

* Handle and send isRecipientUpdate sync messages
* Disable sending isRecipientUpdates for now
This commit is contained in:
Scott Nonnenberg 2019-05-09 08:38:05 -07:00
parent 8c365b1a3a
commit 13ad4abaea
5 changed files with 128 additions and 56 deletions

View file

@ -3,6 +3,7 @@
_, _,
Backbone, Backbone,
ConversationController, ConversationController,
MessageController,
getAccountManager, getAccountManager,
Signal, Signal,
storage, storage,
@ -1134,40 +1135,6 @@
? getGroupDescriptor(message.group) ? getGroupDescriptor(message.group)
: { type: Message.PRIVATE, id: source }; : { type: Message.PRIVATE, id: source };
function createMessageHandler({
createMessage,
getMessageDescriptor,
handleProfileUpdate,
}) {
return async event => {
const { data, confirm } = event;
const messageDescriptor = getMessageDescriptor(data);
const { PROFILE_KEY_UPDATE } = textsecure.protobuf.DataMessage.Flags;
// eslint-disable-next-line no-bitwise
const isProfileUpdate = Boolean(data.message.flags & PROFILE_KEY_UPDATE);
if (isProfileUpdate) {
return handleProfileUpdate({ data, confirm, messageDescriptor });
}
const message = await createMessage(data);
const isDuplicate = await isMessageDuplicate(message);
if (isDuplicate) {
window.log.warn('Received duplicate message', message.idForLogging());
return event.confirm();
}
await ConversationController.getOrCreateAndWait(
messageDescriptor.id,
messageDescriptor.type
);
return message.handleDataMessage(data.message, event.confirm, {
initialLoadComplete,
});
};
}
// Received: // Received:
async function handleMessageReceivedProfileUpdate({ async function handleMessageReceivedProfileUpdate({
data, data,
@ -1186,11 +1153,37 @@
return confirm(); return confirm();
} }
const onMessageReceived = createMessageHandler({ async function onMessageReceived(event) {
handleProfileUpdate: handleMessageReceivedProfileUpdate, const { data, confirm } = event;
getMessageDescriptor: getDescriptorForReceived,
createMessage: initIncomingMessage, const messageDescriptor = getDescriptorForReceived(data);
const { PROFILE_KEY_UPDATE } = textsecure.protobuf.DataMessage.Flags;
// eslint-disable-next-line no-bitwise
const isProfileUpdate = Boolean(data.message.flags & PROFILE_KEY_UPDATE);
if (isProfileUpdate) {
return handleMessageReceivedProfileUpdate({
data,
confirm,
messageDescriptor,
}); });
}
const message = await initIncomingMessage(data);
const isDuplicate = await isMessageDuplicate(message);
if (isDuplicate) {
window.log.warn('Received duplicate message', message.idForLogging());
return event.confirm();
}
await ConversationController.getOrCreateAndWait(
messageDescriptor.id,
messageDescriptor.type
);
return message.handleDataMessage(data.message, event.confirm, {
initialLoadComplete,
});
}
// Sent: // Sent:
async function handleMessageSentProfileUpdate({ async function handleMessageSentProfileUpdate({
@ -1251,26 +1244,93 @@
}); });
} }
const onSentMessage = createMessageHandler({ async function onSentMessage(event) {
handleProfileUpdate: handleMessageSentProfileUpdate, const { data, confirm } = event;
getMessageDescriptor: getDescriptorForSent,
createMessage: createSentMessage,
});
async function isMessageDuplicate(message) { const messageDescriptor = getDescriptorForSent(data);
const { PROFILE_KEY_UPDATE } = textsecure.protobuf.DataMessage.Flags;
// eslint-disable-next-line no-bitwise
const isProfileUpdate = Boolean(data.message.flags & PROFILE_KEY_UPDATE);
if (isProfileUpdate) {
await handleMessageSentProfileUpdate({
data,
confirm,
messageDescriptor,
});
return;
}
const message = await createSentMessage(data);
const existing = await getExistingMessage(message);
const isUpdate = Boolean(data.isRecipientUpdate);
if (isUpdate && existing) {
event.confirm();
let sentTo = [];
let unidentifiedDeliveries = [];
if (Array.isArray(data.unidentifiedStatus)) {
sentTo = data.unidentifiedStatus.map(item => item.destination);
const unidentified = _.filter(data.unidentifiedStatus, item =>
Boolean(item.unidentified)
);
unidentifiedDeliveries = unidentified.map(item => item.destination);
}
existing.set({
sent_to: _.union(existing.get('sent_to'), sentTo),
unidentifiedDeliveries: _.union(
existing.get('unidentifiedDeliveries'),
unidentifiedDeliveries
),
});
await window.Signal.Data.saveMessage(existing.attributes, {
Message: Whisper.Message,
});
} else if (isUpdate) {
window.log.warn(
`onSentMessage: Received update transcript, but no existing entry for message ${message.idForLogging()}. Dropping.`
);
} else if (existing) {
window.log.warn(
`onSentMessage: Received duplicate transcript for message ${message.idForLogging()}, but it was not an update transcript. Dropping.`
);
} else {
await ConversationController.getOrCreateAndWait(
messageDescriptor.id,
messageDescriptor.type
);
await message.handleDataMessage(data.message, event.confirm, {
initialLoadComplete,
});
}
}
async function getExistingMessage(message) {
try { try {
const { attributes } = message; const { attributes } = message;
const result = await window.Signal.Data.getMessageBySender(attributes, { const result = await window.Signal.Data.getMessageBySender(attributes, {
Message: Whisper.Message, Message: Whisper.Message,
}); });
return Boolean(result); if (result) {
return MessageController.register(result.id, result);
}
return null;
} catch (error) { } catch (error) {
window.log.error('isMessageDuplicate error:', Errors.toLogFormat(error)); window.log.error('getExistingMessage error:', Errors.toLogFormat(error));
return false; return false;
} }
} }
async function isMessageDuplicate(message) {
const result = await getExistingMessage(message);
return Boolean(result);
}
async function initIncomingMessage(data, options = {}) { async function initIncomingMessage(data, options = {}) {
const { isError } = options; const { isError } = options;

View file

@ -1113,7 +1113,7 @@
this.trigger('done'); this.trigger('done');
// This is used by sendSyncMessage, then set to null // This is used by sendSyncMessage, then set to null
if (!this.get('synced') && result.dataMessage) { if (result.dataMessage) {
this.set({ dataMessage: result.dataMessage }); this.set({ dataMessage: result.dataMessage });
} }
@ -1244,9 +1244,16 @@
this.syncPromise = this.syncPromise || Promise.resolve(); this.syncPromise = this.syncPromise || Promise.resolve();
const next = () => { const next = () => {
const dataMessage = this.get('dataMessage'); const dataMessage = this.get('dataMessage');
if (this.get('synced') || !dataMessage) { if (!dataMessage) {
return Promise.resolve(); return Promise.resolve();
} }
const isUpdate = Boolean(this.get('synced'));
// Until isRecipientUpdate sync messages are widely supported, will not send them
if (isUpdate) {
return Promise.resolve();
}
return wrap( return wrap(
textsecure.messaging.sendSyncMessage( textsecure.messaging.sendSyncMessage(
dataMessage, dataMessage,
@ -1255,6 +1262,7 @@
this.get('expirationStartTimestamp'), this.get('expirationStartTimestamp'),
this.get('sent_to'), this.get('sent_to'),
this.get('unidentifiedDeliveries'), this.get('unidentifiedDeliveries'),
isUpdate,
sendOptions sendOptions
) )
).then(result => { ).then(result => {

View file

@ -865,12 +865,14 @@ MessageReceiver.prototype.extend({
throw e; throw e;
} }
}, },
handleSentMessage(envelope, sentContainer, msg) { handleSentMessage(envelope, sentContainer) {
const { const {
destination, destination,
timestamp, timestamp,
message: msg,
expirationStartTimestamp, expirationStartTimestamp,
unidentifiedStatus, unidentifiedStatus,
isRecipientUpdate,
} = sentContainer; } = sentContainer;
let p = Promise.resolve(); let p = Promise.resolve();
@ -905,6 +907,7 @@ MessageReceiver.prototype.extend({
device: envelope.sourceDevice, device: envelope.sourceDevice,
unidentifiedStatus, unidentifiedStatus,
message, message,
isRecipientUpdate,
}; };
if (expirationStartTimestamp) { if (expirationStartTimestamp) {
ev.data.expirationStartTimestamp = expirationStartTimestamp.toNumber(); ev.data.expirationStartTimestamp = expirationStartTimestamp.toNumber();
@ -1090,7 +1093,7 @@ MessageReceiver.prototype.extend({
'from', 'from',
this.getEnvelopeId(envelope) this.getEnvelopeId(envelope)
); );
return this.handleSentMessage(envelope, sentMessage, sentMessage.message); return this.handleSentMessage(envelope, sentMessage);
} else if (syncMessage.contacts) { } else if (syncMessage.contacts) {
return this.handleContacts(envelope, syncMessage.contacts); return this.handleContacts(envelope, syncMessage.contacts);
} else if (syncMessage.groups) { } else if (syncMessage.groups) {

View file

@ -23,7 +23,6 @@ function Message(options) {
this.flags = options.flags; this.flags = options.flags;
this.recipients = options.recipients; this.recipients = options.recipients;
this.timestamp = options.timestamp; this.timestamp = options.timestamp;
this.needsSync = options.needsSync;
this.expireTimer = options.expireTimer; this.expireTimer = options.expireTimer;
this.profileKey = options.profileKey; this.profileKey = options.profileKey;
@ -438,6 +437,7 @@ MessageSender.prototype = {
expirationStartTimestamp, expirationStartTimestamp,
sentTo = [], sentTo = [],
unidentifiedDeliveries = [], unidentifiedDeliveries = [],
isUpdate = false,
options options
) { ) {
const myNumber = textsecure.storage.user.getNumber(); const myNumber = textsecure.storage.user.getNumber();
@ -468,6 +468,10 @@ MessageSender.prototype = {
Object.create(null) Object.create(null)
); );
if (isUpdate) {
syncMessage.isRecipientUpdate = true;
}
// Though this field has 'unidenified' in the name, it should have entries for each // Though this field has 'unidenified' in the name, it should have entries for each
// number we sent to. // number we sent to.
if (sentTo && sentTo.length) { if (sentTo && sentTo.length) {
@ -841,7 +845,6 @@ MessageSender.prototype = {
attachments, attachments,
quote, quote,
preview, preview,
needsSync: true,
expireTimer, expireTimer,
profileKey, profileKey,
}, },
@ -939,7 +942,6 @@ MessageSender.prototype = {
attachments, attachments,
quote, quote,
preview, preview,
needsSync: true,
expireTimer, expireTimer,
profileKey, profileKey,
group: { group: {
@ -1054,7 +1056,6 @@ MessageSender.prototype = {
const attrs = { const attrs = {
recipients: numbers, recipients: numbers,
timestamp, timestamp,
needsSync: true,
expireTimer, expireTimer,
profileKey, profileKey,
flags: textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE, flags: textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,
@ -1087,7 +1088,6 @@ MessageSender.prototype = {
{ {
recipients: [number], recipients: [number],
timestamp, timestamp,
needsSync: true,
expireTimer, expireTimer,
profileKey, profileKey,
flags: textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE, flags: textsecure.protobuf.DataMessage.Flags.EXPIRATION_TIMER_UPDATE,

View file

@ -224,6 +224,7 @@ message SyncMessage {
optional DataMessage message = 3; optional DataMessage message = 3;
optional uint64 expirationStartTimestamp = 4; optional uint64 expirationStartTimestamp = 4;
repeated UnidentifiedDeliveryStatus unidentifiedStatus = 5; repeated UnidentifiedDeliveryStatus unidentifiedStatus = 5;
optional bool isRecipientUpdate = 6 [default = false];
} }
message Contacts { message Contacts {