Quellcode durchsuchen

Add replayable error for signed key failure

Disable message sending if signed key updates fail too many times, but
allow the user to retry sending.

// FREEBIE
lilia vor 7 Jahren
Ursprung
Commit
cd0fe7037b
5 geänderte Dateien mit 77 neuen und 7 gelöschten Zeilen
  1. 31 0
      js/libtextsecure.js
  2. 13 2
      js/models/messages.js
  3. 2 5
      js/views/message_view.js
  4. 13 0
      libtextsecure/errors.js
  5. 18 0
      libtextsecure/sendmessage.js

+ 31 - 0
js/libtextsecure.js

@@ -11,6 +11,7 @@
         INIT_SESSION: 2,
         TRANSMIT_MESSAGE: 3,
         REBUILD_MESSAGE: 4,
+        RETRY_SEND_MESSAGE_PROTO: 5
     };
     window.textsecure = window.textsecure || {};
     window.textsecure.replay = {
@@ -89,6 +90,17 @@
     SendMessageNetworkError.prototype = new ReplayableError();
     SendMessageNetworkError.prototype.constructor = SendMessageNetworkError;
 
+    function SignedPreKeyRotationError(numbers, message, timestamp) {
+        ReplayableError.call(this, {
+            functionCode : Type.RETRY_SEND_MESSAGE_PROTO,
+            args         : [numbers, message, timestamp]
+        });
+        this.name = 'SignedPreKeyRotationError';
+        this.message = "Too many signed prekey rotation failures";
+    }
+    SignedPreKeyRotationError.prototype = new ReplayableError();
+    SignedPreKeyRotationError.prototype.constructor = SignedPreKeyRotationError;
+
     function MessageError(message, httpError) {
         ReplayableError.call(this, {
             functionCode : Type.REBUILD_MESSAGE,
@@ -119,6 +131,7 @@
     window.textsecure.ReplayableError = ReplayableError;
     window.textsecure.OutgoingMessageError = OutgoingMessageError;
     window.textsecure.MessageError = MessageError;
+    window.textsecure.SignedPreKeyRotationError = SignedPreKeyRotationError;
 
 })();
 
@@ -39014,6 +39027,11 @@ MessageSender.prototype = {
         }.bind(this));
     },
     sendMessageProto: function(timestamp, numbers, message, callback) {
+        var rejections = textsecure.storage.get('signedKeyRotationRejected', 0);
+        if (rejections > 5) {
+            throw new textsecure.SignedPreKeyRotationError(numbers, message.toArrayBuffer(), timestamp);
+        }
+
         var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback);
 
         numbers.forEach(function(number) {
@@ -39023,6 +39041,18 @@ MessageSender.prototype = {
         }.bind(this));
     },
 
+    retrySendMessageProto: function(numbers, encodedMessage, timestamp) {
+        var proto = textsecure.protobuf.DataMessage.decode(encodedMessage);
+        return new Promise(function(resolve, reject) {
+            this.sendMessageProto(timestamp, numbers, proto, function(res) {
+                if (res.errors.length > 0)
+                    reject(res);
+                else
+                    resolve(res);
+            });
+        }.bind(this));
+    },
+
     sendIndividualProto: function(number, proto, timestamp) {
         return new Promise(function(resolve, reject) {
             this.sendMessageProto(timestamp, [number], proto, function(res) {
@@ -39330,6 +39360,7 @@ textsecure.MessageSender = function(url, ports, username, password, attachment_s
     textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE);
     textsecure.replay.registerFunction(sender.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE);
     textsecure.replay.registerFunction(sender.sendMessage.bind(sender), textsecure.replay.Type.REBUILD_MESSAGE);
+    textsecure.replay.registerFunction(sender.retrySendMessageProto.bind(sender), textsecure.replay.Type.RETRY_SEND_MESSAGE_PROTO);
 
     this.sendExpirationTimerUpdateToNumber = sender.sendExpirationTimerUpdateToNumber.bind(sender);
     this.sendExpirationTimerUpdateToGroup  = sender.sendExpirationTimerUpdateToGroup .bind(sender);

+ 13 - 2
js/models/messages.js

@@ -211,6 +211,9 @@
                 if (result instanceof Error) {
                     errors = [result];
                     this.saveErrors(errors);
+                    if (result.name === 'SignedPreKeyRotationError') {
+                        getAccountManager().rotateSignedPreKey();
+                    }
                 } else {
                     errors = result.errors;
                     this.saveErrors(errors);
@@ -283,7 +286,8 @@
             var error = _.find(this.get('errors'), function(e) {
                 return (e.name === 'MessageError' ||
                         e.name === 'OutgoingMessageError' ||
-                        e.name === 'SendMessageNetworkError');
+                        e.name === 'SendMessageNetworkError' ||
+                        e.name === 'SignedPreKeyRotationError');
             });
             return !!error;
         },
@@ -292,11 +296,18 @@
                 return e.number === number &&
                     (e.name === 'MessageError' ||
                      e.name === 'OutgoingMessageError' ||
-                     e.name === 'SendMessageNetworkError');
+                     e.name === 'SendMessageNetworkError' ||
+                     e.name === 'SignedPreKeyRotationError');
             });
             this.set({errors: errors[1]});
             return errors[0][0];
         },
+        isReplayableError: function(e) {
+            return (e.name === 'MessageError' ||
+                    e.name === 'OutgoingMessageError' ||
+                    e.name === 'SendMessageNetworkError' ||
+                    e.name === 'SignedPreKeyRotationError');
+        },
 
         resend: function(number) {
             var error = this.removeOutgoingErrors(number);

+ 2 - 5
js/views/message_view.js

@@ -131,11 +131,8 @@
             'click .error-message': 'select'
         },
         retryMessage: function() {
-            var retrys = _.filter(this.model.get('errors'), function(e) {
-                return (e.name === 'MessageError' ||
-                        e.name === 'OutgoingMessageError' ||
-                        e.name === 'SendMessageNetworkError');
-            });
+            var retrys = _.filter(this.model.get('errors'),
+                    this.model.isReplayableError.bind(this.model));
             _.map(retrys, 'number').forEach(function(number) {
                 this.model.resend(number);
             }.bind(this));

+ 13 - 0
libtextsecure/errors.js

@@ -10,6 +10,7 @@
         INIT_SESSION: 2,
         TRANSMIT_MESSAGE: 3,
         REBUILD_MESSAGE: 4,
+        RETRY_SEND_MESSAGE_PROTO: 5
     };
     window.textsecure = window.textsecure || {};
     window.textsecure.replay = {
@@ -88,6 +89,17 @@
     SendMessageNetworkError.prototype = new ReplayableError();
     SendMessageNetworkError.prototype.constructor = SendMessageNetworkError;
 
+    function SignedPreKeyRotationError(numbers, message, timestamp) {
+        ReplayableError.call(this, {
+            functionCode : Type.RETRY_SEND_MESSAGE_PROTO,
+            args         : [numbers, message, timestamp]
+        });
+        this.name = 'SignedPreKeyRotationError';
+        this.message = "Too many signed prekey rotation failures";
+    }
+    SignedPreKeyRotationError.prototype = new ReplayableError();
+    SignedPreKeyRotationError.prototype.constructor = SignedPreKeyRotationError;
+
     function MessageError(message, httpError) {
         ReplayableError.call(this, {
             functionCode : Type.REBUILD_MESSAGE,
@@ -118,5 +130,6 @@
     window.textsecure.ReplayableError = ReplayableError;
     window.textsecure.OutgoingMessageError = OutgoingMessageError;
     window.textsecure.MessageError = MessageError;
+    window.textsecure.SignedPreKeyRotationError = SignedPreKeyRotationError;
 
 })();

+ 18 - 0
libtextsecure/sendmessage.js

@@ -183,6 +183,11 @@ MessageSender.prototype = {
         }.bind(this));
     },
     sendMessageProto: function(timestamp, numbers, message, callback) {
+        var rejections = textsecure.storage.get('signedKeyRotationRejected', 0);
+        if (rejections > 5) {
+            throw new textsecure.SignedPreKeyRotationError(numbers, message.toArrayBuffer(), timestamp);
+        }
+
         var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback);
 
         numbers.forEach(function(number) {
@@ -192,6 +197,18 @@ MessageSender.prototype = {
         }.bind(this));
     },
 
+    retrySendMessageProto: function(numbers, encodedMessage, timestamp) {
+        var proto = textsecure.protobuf.DataMessage.decode(encodedMessage);
+        return new Promise(function(resolve, reject) {
+            this.sendMessageProto(timestamp, numbers, proto, function(res) {
+                if (res.errors.length > 0)
+                    reject(res);
+                else
+                    resolve(res);
+            });
+        }.bind(this));
+    },
+
     sendIndividualProto: function(number, proto, timestamp) {
         return new Promise(function(resolve, reject) {
             this.sendMessageProto(timestamp, [number], proto, function(res) {
@@ -499,6 +516,7 @@ textsecure.MessageSender = function(url, ports, username, password, attachment_s
     textsecure.replay.registerFunction(sender.tryMessageAgain.bind(sender), textsecure.replay.Type.ENCRYPT_MESSAGE);
     textsecure.replay.registerFunction(sender.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE);
     textsecure.replay.registerFunction(sender.sendMessage.bind(sender), textsecure.replay.Type.REBUILD_MESSAGE);
+    textsecure.replay.registerFunction(sender.retrySendMessageProto.bind(sender), textsecure.replay.Type.RETRY_SEND_MESSAGE_PROTO);
 
     this.sendExpirationTimerUpdateToNumber = sender.sendExpirationTimerUpdateToNumber.bind(sender);
     this.sendExpirationTimerUpdateToGroup  = sender.sendExpirationTimerUpdateToGroup .bind(sender);