From 7b6820d2ac7f460b815fb85c19b322d63fb9871a Mon Sep 17 00:00:00 2001 From: lilia Date: Tue, 17 Nov 2015 12:00:41 -0800 Subject: [PATCH] Refactor messageSender/OutgoingMessage Move encrypt and transmit to OutgoingMessage Restore per-number queueing to messageSender // FREEBIE --- js/libtextsecure.js | 162 +++++++++++++++--------------- libtextsecure/api.js | 5 +- libtextsecure/outgoing_message.js | 81 ++++++++++----- libtextsecure/sendmessage.js | 76 +++++--------- 4 files changed, 166 insertions(+), 158 deletions(-) diff --git a/js/libtextsecure.js b/js/libtextsecure.js index c01f8e294..125285de8 100644 --- a/js/libtextsecure.js +++ b/js/libtextsecure.js @@ -36412,9 +36412,8 @@ var TextSecureServer = (function() { return res; }); }, - sendMessages: function(destination, messageArray) { - var jsonData = { messages: messageArray }; - jsonData.timestamp = messageArray[0].timestamp; + sendMessages: function(destination, messageArray, timestamp) { + var jsonData = { messages: messageArray, timestamp: timestamp}; return this.ajax({ call : 'messages', @@ -37093,13 +37092,13 @@ textsecure.MessageReceiver.prototype = { /* * vim: ts=4:sw=4:expandtab */ -function OutgoingMessage(timestamp, numbers, message, callback, messageSender) { +function OutgoingMessage(server, timestamp, numbers, message, callback) { + this.server = server; this.timestamp = timestamp; this.numbers = numbers; - this.message = message; - this.sender = messageSender; - this.server = messageSender.server; + this.message = message; // DataMessage or ContentMessage proto this.callback = callback; + this.legacy = (message instanceof textsecure.protobuf.DataMessage); this.numbersCompleted = 0; this.errors = []; @@ -37127,8 +37126,15 @@ OutgoingMessage.prototype = { reloadDevicesAndSend: function(number, recurse) { return function() { return textsecure.storage.devices.getDeviceObjectsForNumber(number).then(function(devicesForNumber) { - if (devicesForNumber.length == 0) + if (devicesForNumber.length == 0) { return this.registerError(number, "Got empty device list when loading device keys", null); + } + var relay = devicesForNumber[0].relay; + for (var i=1; i < devicesForNumber.length; ++i) { + if (devicesForNumber[i].relay !== relay) { + throw new Error("Mismatched relays for number " + number); + } + } return this.doSendMessage(number, devicesForNumber, recurse); }.bind(this)); }.bind(this); @@ -37171,9 +37177,20 @@ OutgoingMessage.prototype = { } }, + transmitMessage: function(number, jsonData, timestamp) { + return this.server.sendMessages(number, jsonData, timestamp).catch(function(e) { + if (e.name === 'HTTPError' && (e.code !== 409 && e.code !== 410)) { + // 409 and 410 should bubble and be handled by doSendMessage + // all other network errors can be retried later. + throw new textsecure.SendMessageNetworkError(number, jsonData, e); + } + throw e; + }); + }, + doSendMessage: function(number, devicesForNumber, recurse) { - return this.sender.encryptToDevices(this.timestamp, number, devicesForNumber, this.message).then(function(jsonData) { - return this.sender.transmitMessage(number, jsonData).then(function() { + return this.encryptToDevices(devicesForNumber).then(function(jsonData) { + return this.transmitMessage(number, jsonData, this.timestamp).then(function() { this.successfulNumbers[this.successfulNumbers.length] = number; this.numberCompleted(); }.bind(this)); @@ -37205,6 +37222,39 @@ OutgoingMessage.prototype = { }.bind(this)); }, + encryptToDevices: function(deviceObjectList) { + var plaintext = this.message.toArrayBuffer(); + return Promise.all(deviceObjectList.map(function(device) { + return textsecure.protocol_wrapper.encryptMessageFor(device, plaintext).then(function(encryptedMsg) { + var json = this.toJSON(device, encryptedMsg); + return textsecure.storage.devices.removeTempKeysFromDevice(device.encodedNumber).then(function() { + return json; + }); + }.bind(this)); + }.bind(this))); + }, + + toJSON: function(device, encryptedMsg) { + var json = { + type: encryptedMsg.type, + destinationDeviceId: textsecure.utils.unencodeNumber(device.encodedNumber)[1], + destinationRegistrationId: device.registrationId + }; + + if (device.relay !== undefined) { + json.relay = device.relay; + } + + var content = btoa(encryptedMsg.body); + if (this.legacy) { + json.body = content; + } else { + json.content = content; + } + + return json; + }, + sendToNumber: function(number) { return textsecure.storage.devices.getStaleDeviceIdsForNumber(number).then(function(updateDevices) { return this.getKeysForNumber(number, updateDevices) @@ -37213,22 +37263,6 @@ OutgoingMessage.prototype = { this.registerError(number, "Failed to retreive new device keys for number " + number, error); }.bind(this)); }.bind(this)); - }, - - send: function() { - this.numbers.forEach(function(number) { - var sendPrevious = this.sender.pendingMessages[number] || Promise.resolve(); - var sendCurrent = this.sender.pendingMessages[number] = sendPrevious.then(function() { - return this.sendToNumber(number); - }.bind(this)).catch(function() { - return this.sendToNumber(number); - }.bind(this)); - sendCurrent.then(function() { - if (this.sender.pendingMessages[number] === sendCurrent) { - delete this.sender.pendingMessages[number]; - } - }.bind(this)); - }.bind(this)); } }; @@ -37242,56 +37276,6 @@ function MessageSender(url, username, password, attachment_server_url) { MessageSender.prototype = { constructor: MessageSender, - // message == DataMessage or ContentMessage proto - encryptToDevices: function(timestamp, number, deviceObjectList, message) { - var legacy = (message instanceof textsecure.protobuf.DataMessage); - var plaintext = message.toArrayBuffer(); - var relay = deviceObjectList[0].relay; - for (var i=1; i < deviceObjectList.length; ++i) { - if (deviceObjectList[i].relay !== relay) { - throw new Error("Mismatched relays for number " + number); - } - } - return Promise.all(deviceObjectList.map(function(device) { - return textsecure.protocol_wrapper.encryptMessageFor(device, plaintext).then(function(encryptedMsg) { - return textsecure.protocol_wrapper.getRegistrationId(device.encodedNumber).then(function(registrationId) { - return textsecure.storage.devices.removeTempKeysFromDevice(device.encodedNumber).then(function() { - var json = { - type: encryptedMsg.type, - destinationDeviceId: textsecure.utils.unencodeNumber(device.encodedNumber)[1], - destinationRegistrationId: registrationId, - timestamp: timestamp - }; - - if (device.relay !== undefined) { - json.relay = device.relay; - } - - var content = btoa(encryptedMsg.body); - if (legacy) { - json.body = content; - } else { - json.content = content; - } - - return json; - }); - }); - }); - })); - }, - - transmitMessage: function(number, jsonData) { - return this.server.sendMessages(number, jsonData).catch(function(e) { - if (e.name === 'HTTPError' && (e.code !== 409 && e.code !== 410)) { - // 409 and 410 should bubble and be handled by doSendMessage - // all other network errors can be retried later. - throw new textsecure.SendMessageNetworkError(number, jsonData, e); - } - throw e; - }); - }, - makeAttachmentPointer: function(attachment) { if (typeof attachment !== 'object' || attachment == null) { return Promise.resolve(undefined); @@ -37309,14 +37293,34 @@ MessageSender.prototype = { }.bind(this)); }, + retransmitMessage: function(number, jsonData, timestamp) { + var outgoing = new OutgoingMessage(this.server); + return outgoing.transmitMessage(number, jsonData, timestamp); + }, + tryMessageAgain: function(number, encodedMessage, timestamp) { var proto = textsecure.protobuf.DataMessage.decode(encodedMessage); return this.sendIndividualProto(number, proto, timestamp); }, + queueJobForNumber: function(number, runJob) { + var runPrevious = this.pendingMessages[number] || Promise.resolve(); + var runCurrent = this.pendingMessages[number] = runPrevious.then(runJob, runJob); + runCurrent.then(function() { + if (this.pendingMessages[number] === runCurrent) { + delete this.pendingMessages[number]; + } + }.bind(this)); + }, + sendMessageProto: function(timestamp, numbers, message, callback) { - var outgoing = new OutgoingMessage(timestamp, numbers, message, callback, this); - outgoing.send(); + var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback); + + numbers.forEach(function(number) { + this.queueJobForNumber(number, function() { + return outgoing.sendToNumber(number); + }); + }.bind(this)); }, sendIndividualProto: function(number, proto, timestamp) { @@ -37556,7 +37560,7 @@ window.textsecure = window.textsecure || {}; textsecure.MessageSender = function(url, username, password, attachment_server_url) { var sender = new MessageSender(url, username, password, attachment_server_url); textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE); - textsecure.replay.registerFunction(sender.transmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE); + textsecure.replay.registerFunction(sender.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE); this.sendRequestGroupSyncMessage = sender.sendRequestGroupSyncMessage .bind(sender); this.sendRequestContactSyncMessage = sender.sendRequestContactSyncMessage.bind(sender); diff --git a/libtextsecure/api.js b/libtextsecure/api.js index 7de6ebba1..0ddb4094c 100644 --- a/libtextsecure/api.js +++ b/libtextsecure/api.js @@ -255,9 +255,8 @@ var TextSecureServer = (function() { return res; }); }, - sendMessages: function(destination, messageArray) { - var jsonData = { messages: messageArray }; - jsonData.timestamp = messageArray[0].timestamp; + sendMessages: function(destination, messageArray, timestamp) { + var jsonData = { messages: messageArray, timestamp: timestamp}; return this.ajax({ call : 'messages', diff --git a/libtextsecure/outgoing_message.js b/libtextsecure/outgoing_message.js index aa124a093..c5596ba0c 100644 --- a/libtextsecure/outgoing_message.js +++ b/libtextsecure/outgoing_message.js @@ -1,13 +1,13 @@ /* * vim: ts=4:sw=4:expandtab */ -function OutgoingMessage(timestamp, numbers, message, callback, messageSender) { +function OutgoingMessage(server, timestamp, numbers, message, callback) { + this.server = server; this.timestamp = timestamp; this.numbers = numbers; - this.message = message; - this.sender = messageSender; - this.server = messageSender.server; + this.message = message; // DataMessage or ContentMessage proto this.callback = callback; + this.legacy = (message instanceof textsecure.protobuf.DataMessage); this.numbersCompleted = 0; this.errors = []; @@ -35,8 +35,15 @@ OutgoingMessage.prototype = { reloadDevicesAndSend: function(number, recurse) { return function() { return textsecure.storage.devices.getDeviceObjectsForNumber(number).then(function(devicesForNumber) { - if (devicesForNumber.length == 0) + if (devicesForNumber.length == 0) { return this.registerError(number, "Got empty device list when loading device keys", null); + } + var relay = devicesForNumber[0].relay; + for (var i=1; i < devicesForNumber.length; ++i) { + if (devicesForNumber[i].relay !== relay) { + throw new Error("Mismatched relays for number " + number); + } + } return this.doSendMessage(number, devicesForNumber, recurse); }.bind(this)); }.bind(this); @@ -79,9 +86,20 @@ OutgoingMessage.prototype = { } }, + transmitMessage: function(number, jsonData, timestamp) { + return this.server.sendMessages(number, jsonData, timestamp).catch(function(e) { + if (e.name === 'HTTPError' && (e.code !== 409 && e.code !== 410)) { + // 409 and 410 should bubble and be handled by doSendMessage + // all other network errors can be retried later. + throw new textsecure.SendMessageNetworkError(number, jsonData, e); + } + throw e; + }); + }, + doSendMessage: function(number, devicesForNumber, recurse) { - return this.sender.encryptToDevices(this.timestamp, number, devicesForNumber, this.message).then(function(jsonData) { - return this.sender.transmitMessage(number, jsonData).then(function() { + return this.encryptToDevices(devicesForNumber).then(function(jsonData) { + return this.transmitMessage(number, jsonData, this.timestamp).then(function() { this.successfulNumbers[this.successfulNumbers.length] = number; this.numberCompleted(); }.bind(this)); @@ -113,6 +131,39 @@ OutgoingMessage.prototype = { }.bind(this)); }, + encryptToDevices: function(deviceObjectList) { + var plaintext = this.message.toArrayBuffer(); + return Promise.all(deviceObjectList.map(function(device) { + return textsecure.protocol_wrapper.encryptMessageFor(device, plaintext).then(function(encryptedMsg) { + var json = this.toJSON(device, encryptedMsg); + return textsecure.storage.devices.removeTempKeysFromDevice(device.encodedNumber).then(function() { + return json; + }); + }.bind(this)); + }.bind(this))); + }, + + toJSON: function(device, encryptedMsg) { + var json = { + type: encryptedMsg.type, + destinationDeviceId: textsecure.utils.unencodeNumber(device.encodedNumber)[1], + destinationRegistrationId: device.registrationId + }; + + if (device.relay !== undefined) { + json.relay = device.relay; + } + + var content = btoa(encryptedMsg.body); + if (this.legacy) { + json.body = content; + } else { + json.content = content; + } + + return json; + }, + sendToNumber: function(number) { return textsecure.storage.devices.getStaleDeviceIdsForNumber(number).then(function(updateDevices) { return this.getKeysForNumber(number, updateDevices) @@ -121,21 +172,5 @@ OutgoingMessage.prototype = { this.registerError(number, "Failed to retreive new device keys for number " + number, error); }.bind(this)); }.bind(this)); - }, - - send: function() { - this.numbers.forEach(function(number) { - var sendPrevious = this.sender.pendingMessages[number] || Promise.resolve(); - var sendCurrent = this.sender.pendingMessages[number] = sendPrevious.then(function() { - return this.sendToNumber(number); - }.bind(this)).catch(function() { - return this.sendToNumber(number); - }.bind(this)); - sendCurrent.then(function() { - if (this.sender.pendingMessages[number] === sendCurrent) { - delete this.sender.pendingMessages[number]; - } - }.bind(this)); - }.bind(this)); } }; diff --git a/libtextsecure/sendmessage.js b/libtextsecure/sendmessage.js index b5c491d24..2b96e4ead 100644 --- a/libtextsecure/sendmessage.js +++ b/libtextsecure/sendmessage.js @@ -8,56 +8,6 @@ function MessageSender(url, username, password, attachment_server_url) { MessageSender.prototype = { constructor: MessageSender, - // message == DataMessage or ContentMessage proto - encryptToDevices: function(timestamp, number, deviceObjectList, message) { - var legacy = (message instanceof textsecure.protobuf.DataMessage); - var plaintext = message.toArrayBuffer(); - var relay = deviceObjectList[0].relay; - for (var i=1; i < deviceObjectList.length; ++i) { - if (deviceObjectList[i].relay !== relay) { - throw new Error("Mismatched relays for number " + number); - } - } - return Promise.all(deviceObjectList.map(function(device) { - return textsecure.protocol_wrapper.encryptMessageFor(device, plaintext).then(function(encryptedMsg) { - return textsecure.protocol_wrapper.getRegistrationId(device.encodedNumber).then(function(registrationId) { - return textsecure.storage.devices.removeTempKeysFromDevice(device.encodedNumber).then(function() { - var json = { - type: encryptedMsg.type, - destinationDeviceId: textsecure.utils.unencodeNumber(device.encodedNumber)[1], - destinationRegistrationId: registrationId, - timestamp: timestamp - }; - - if (device.relay !== undefined) { - json.relay = device.relay; - } - - var content = btoa(encryptedMsg.body); - if (legacy) { - json.body = content; - } else { - json.content = content; - } - - return json; - }); - }); - }); - })); - }, - - transmitMessage: function(number, jsonData) { - return this.server.sendMessages(number, jsonData).catch(function(e) { - if (e.name === 'HTTPError' && (e.code !== 409 && e.code !== 410)) { - // 409 and 410 should bubble and be handled by doSendMessage - // all other network errors can be retried later. - throw new textsecure.SendMessageNetworkError(number, jsonData, e); - } - throw e; - }); - }, - makeAttachmentPointer: function(attachment) { if (typeof attachment !== 'object' || attachment == null) { return Promise.resolve(undefined); @@ -75,14 +25,34 @@ MessageSender.prototype = { }.bind(this)); }, + retransmitMessage: function(number, jsonData, timestamp) { + var outgoing = new OutgoingMessage(this.server); + return outgoing.transmitMessage(number, jsonData, timestamp); + }, + tryMessageAgain: function(number, encodedMessage, timestamp) { var proto = textsecure.protobuf.DataMessage.decode(encodedMessage); return this.sendIndividualProto(number, proto, timestamp); }, + queueJobForNumber: function(number, runJob) { + var runPrevious = this.pendingMessages[number] || Promise.resolve(); + var runCurrent = this.pendingMessages[number] = runPrevious.then(runJob, runJob); + runCurrent.then(function() { + if (this.pendingMessages[number] === runCurrent) { + delete this.pendingMessages[number]; + } + }.bind(this)); + }, + sendMessageProto: function(timestamp, numbers, message, callback) { - var outgoing = new OutgoingMessage(timestamp, numbers, message, callback, this); - outgoing.send(); + var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback); + + numbers.forEach(function(number) { + this.queueJobForNumber(number, function() { + return outgoing.sendToNumber(number); + }); + }.bind(this)); }, sendIndividualProto: function(number, proto, timestamp) { @@ -322,7 +292,7 @@ window.textsecure = window.textsecure || {}; textsecure.MessageSender = function(url, username, password, attachment_server_url) { var sender = new MessageSender(url, username, password, attachment_server_url); textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE); - textsecure.replay.registerFunction(sender.transmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE); + textsecure.replay.registerFunction(sender.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE); this.sendRequestGroupSyncMessage = sender.sendRequestGroupSyncMessage .bind(sender); this.sendRequestContactSyncMessage = sender.sendRequestContactSyncMessage.bind(sender);