From 07702c4ee5049e600b9e75a8d0d990dc0630e5df Mon Sep 17 00:00:00 2001 From: lilia Date: Thu, 19 Nov 2015 16:57:48 -0800 Subject: [PATCH] Let the application layer send sync messages Previously, libtextsecure would send a sync message automatically when appropriate. This fails if any recipient has a key conflict or if our network connection fails mid-send. Instead, when appropriate, return a the DataMessage encoded as an array buffer for later syncing. This lets the application choose when to send it, which we now do after any successful send to a recipient, rather than after all recipients are successfully sent to. Eventually we should move the DataMessage protobuf construction and group sending logic to the application layer entirely, in which case we wouldn't need libtextsecure to construct the sync message either. Fixes #408 --- js/libtextsecure.js | 20 +++++++++++++------- js/models/conversations.js | 3 +++ js/models/messages.js | 27 +++++++++++++++++++++++++-- libtextsecure/sendmessage.js | 20 +++++++++++++------- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/js/libtextsecure.js b/js/libtextsecure.js index cac599ffe..017571e0b 100644 --- a/js/libtextsecure.js +++ b/js/libtextsecure.js @@ -37334,13 +37334,14 @@ MessageSender.prototype = { }.bind(this)); }, - sendSyncMessage: function(message, timestamp, destination) { + sendSyncMessage: function(encodedDataMessage, timestamp, destination) { var myNumber = textsecure.storage.user.getNumber(); var myDevice = textsecure.storage.user.getDeviceId(); if (myDevice != 1) { + var dataMessage = textsecure.protobuf.DataMessage.decode(encodedDataMessage); var sentMessage = new textsecure.protobuf.SyncMessage.Sent(); sentMessage.timestamp = timestamp; - sentMessage.message = message; + sentMessage.message = dataMessage; if (destination) { sentMessage.destination = destination; } @@ -37348,7 +37349,6 @@ MessageSender.prototype = { syncMessage.sent = sentMessage; var contentMessage = new textsecure.protobuf.Content(); contentMessage.syncMessage = syncMessage; - return this.sendIndividualProto(myNumber, contentMessage, Date.now()); } }, @@ -37393,12 +37393,13 @@ MessageSender.prototype = { return new Promise(function(resolve, reject) { this.sendMessageProto(timestamp, numbers, proto, function(res) { + res.dataMessage = proto.toArrayBuffer(); if (res.errors.length > 0) reject(res); else resolve(res); - }); - }.bind(this)).then(this.sendSyncMessage.bind(this, proto, timestamp)); + }.bind(this)); + }.bind(this)); }, sendMessageToNumber: function(number, messageText, attachments, timestamp) { @@ -37407,8 +37408,12 @@ MessageSender.prototype = { return Promise.all(attachments.map(this.makeAttachmentPointer.bind(this))).then(function(attachmentsArray) { proto.attachments = attachmentsArray; - return this.sendIndividualProto(number, proto, timestamp).then(function() { - return this.sendSyncMessage(proto, timestamp, number); + return this.sendIndividualProto(number, proto, timestamp).then(function(res) { + res.dataMessage = proto.toArrayBuffer(); + return res; + }.bind(this)).catch(function(res) { + res.dataMessage = proto.toArrayBuffer(); + throw res; }.bind(this)); }.bind(this)); }, @@ -37573,6 +37578,7 @@ textsecure.MessageSender = function(url, username, password, attachment_server_u this.setGroupName = sender.setGroupName .bind(sender); this.setGroupAvatar = sender.setGroupAvatar .bind(sender); this.leaveGroup = sender.leaveGroup .bind(sender); + this.sendSyncMessage = sender.sendSyncMessage .bind(sender); }; textsecure.MessageSender.prototype = { diff --git a/js/models/conversations.js b/js/models/conversations.js index ce684c724..0af25c16f 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -98,6 +98,9 @@ sent_at : now, received_at : now }); + if (this.isPrivate()) { + message.set({destination: this.id}); + } message.save(); this.save({ diff --git a/js/models/messages.js b/js/models/messages.js index 5f4cb12ae..061ddfd37 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -140,13 +140,36 @@ send: function(promise) { this.trigger('pending'); - return promise.then(function() { + return promise.then(function(result) { this.trigger('done'); + if (result.dataMessage) { + this.set({dataMessage: result.dataMessage}); + } this.save({sent: true}); - }.bind(this)).catch(function(errors) { + this.sendSyncMessage(); + }.bind(this)).catch(function(result) { this.trigger('done'); + if (result.dataMessage) { + this.set({dataMessage: result.dataMessage}); + } this.set({sent: true}); this.saveErrors(result.errors); + if (result.successfulNumbers.length > 0) { + this.sendSyncMessage(); + } + }.bind(this)); + }, + + sendSyncMessage: function() { + var dataMessage = this.get('dataMessage'); + if (this.get('synced') || !dataMessage) { + return; + } + + textsecure.messaging.sendSyncMessage( + dataMessage, this.get('sent_at'), this.get('destination') + ).then(function() { + this.save({synced: true, dataMessage: null}); }.bind(this)); }, diff --git a/libtextsecure/sendmessage.js b/libtextsecure/sendmessage.js index 68ea349a7..f3dcb1bcb 100644 --- a/libtextsecure/sendmessage.js +++ b/libtextsecure/sendmessage.js @@ -66,13 +66,14 @@ MessageSender.prototype = { }.bind(this)); }, - sendSyncMessage: function(message, timestamp, destination) { + sendSyncMessage: function(encodedDataMessage, timestamp, destination) { var myNumber = textsecure.storage.user.getNumber(); var myDevice = textsecure.storage.user.getDeviceId(); if (myDevice != 1) { + var dataMessage = textsecure.protobuf.DataMessage.decode(encodedDataMessage); var sentMessage = new textsecure.protobuf.SyncMessage.Sent(); sentMessage.timestamp = timestamp; - sentMessage.message = message; + sentMessage.message = dataMessage; if (destination) { sentMessage.destination = destination; } @@ -80,7 +81,6 @@ MessageSender.prototype = { syncMessage.sent = sentMessage; var contentMessage = new textsecure.protobuf.Content(); contentMessage.syncMessage = syncMessage; - return this.sendIndividualProto(myNumber, contentMessage, Date.now()); } }, @@ -125,12 +125,13 @@ MessageSender.prototype = { return new Promise(function(resolve, reject) { this.sendMessageProto(timestamp, numbers, proto, function(res) { + res.dataMessage = proto.toArrayBuffer(); if (res.errors.length > 0) reject(res); else resolve(res); - }); - }.bind(this)).then(this.sendSyncMessage.bind(this, proto, timestamp)); + }.bind(this)); + }.bind(this)); }, sendMessageToNumber: function(number, messageText, attachments, timestamp) { @@ -139,8 +140,12 @@ MessageSender.prototype = { return Promise.all(attachments.map(this.makeAttachmentPointer.bind(this))).then(function(attachmentsArray) { proto.attachments = attachmentsArray; - return this.sendIndividualProto(number, proto, timestamp).then(function() { - return this.sendSyncMessage(proto, timestamp, number); + return this.sendIndividualProto(number, proto, timestamp).then(function(res) { + res.dataMessage = proto.toArrayBuffer(); + return res; + }.bind(this)).catch(function(res) { + res.dataMessage = proto.toArrayBuffer(); + throw res; }.bind(this)); }.bind(this)); }, @@ -305,6 +310,7 @@ textsecure.MessageSender = function(url, username, password, attachment_server_u this.setGroupName = sender.setGroupName .bind(sender); this.setGroupAvatar = sender.setGroupAvatar .bind(sender); this.leaveGroup = sender.leaveGroup .bind(sender); + this.sendSyncMessage = sender.sendSyncMessage .bind(sender); }; textsecure.MessageSender.prototype = {