From 7e82d1295c577312f23607edbb148b066ee6703a Mon Sep 17 00:00:00 2001 From: lilia Date: Fri, 5 Feb 2016 16:42:53 -0800 Subject: [PATCH] Handle attachment upload errors Adds a new kind of replayable error that handles retry of pre-encryption failures, e.g., attachment upload. Fixes #485 // FREEBIE --- js/libtextsecure.js | 36 ++++++++++++++++++++++++++------- js/models/messages.js | 3 ++- js/views/message_detail_view.js | 6 ++++-- libtextsecure/errors.js | 16 +++++++++++++++ libtextsecure/sendmessage.js | 20 +++++++++++------- 5 files changed, 64 insertions(+), 17 deletions(-) diff --git a/js/libtextsecure.js b/js/libtextsecure.js index 531db954..9586e47c 100644 --- a/js/libtextsecure.js +++ b/js/libtextsecure.js @@ -10,6 +10,7 @@ ENCRYPT_MESSAGE: 1, INIT_SESSION: 2, TRANSMIT_MESSAGE: 3, + REBUILD_MESSAGE: 4, }; window.textsecure = window.textsecure || {}; window.textsecure.replay = { @@ -88,11 +89,26 @@ SendMessageNetworkError.prototype = new ReplayableError(); SendMessageNetworkError.prototype.constructor = SendMessageNetworkError; + function MessageError(message, httpError) { + ReplayableError.call(this, { + functionCode : Type.REBUILD_MESSAGE, + args : [message] + }); + this.name = 'MessageError'; + this.code = httpError.code; + this.message = httpError.message; + this.stack = httpError.stack; + } + MessageError.prototype = new ReplayableError(); + MessageError.prototype.constructor = MessageError; + + window.textsecure.SendMessageNetworkError = SendMessageNetworkError; window.textsecure.IncomingIdentityKeyError = IncomingIdentityKeyError; window.textsecure.OutgoingIdentityKeyError = OutgoingIdentityKeyError; window.textsecure.ReplayableError = ReplayableError; window.textsecure.OutgoingMessageError = OutgoingMessageError; + window.textsecure.MessageError = MessageError; })(); @@ -37432,6 +37448,7 @@ function Message(options) { this.timestamp = options.timestamp; this.needsSync = options.needsSync; } + Message.prototype = { constructor: Message, toProto: function() { @@ -37502,7 +37519,8 @@ MessageSender.prototype = { }.bind(this)); }, - sendMessage: function(message) { + sendMessage: function(attrs) { + var message = new Message(attrs); return Promise.all( message.attachments.map(this.makeAttachmentPointer.bind(this)) ).then(function(attachmentPointers) { @@ -37522,7 +37540,13 @@ MessageSender.prototype = { } ); }.bind(this)); - }.bind(this)); + }.bind(this)).catch(function(error) { + if (error instanceof Error && error.name === 'HTTPError') { + throw new textsecure.MessageError(attrs, error); + } else { + throw error; + } + }); }, sendMessageProto: function(timestamp, numbers, message, callback) { var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback); @@ -37616,15 +37640,13 @@ MessageSender.prototype = { }, sendMessageToNumber: function(number, messageText, attachments, timestamp) { - var message = new Message({ + return this.sendMessage({ recipients : [number], body : messageText, timestamp : timestamp, attachments : attachments, needsSync : true }); - - return this.sendMessage(message); }, closeSession: function(number, timestamp) { @@ -37655,7 +37677,7 @@ MessageSender.prototype = { return Promise.reject(new Error('No other members in the group')); } - var message = new Message({ + return this.sendMessage({ recipients : numbers, body : messageText, timestamp : timestamp, @@ -37666,7 +37688,6 @@ MessageSender.prototype = { type: textsecure.protobuf.GroupContext.Type.DELIVER } }); - return this.sendMessage(message); }.bind(this)); }, @@ -37785,6 +37806,7 @@ textsecure.MessageSender = function(url, username, password, attachment_server_u 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.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE); + textsecure.replay.registerFunction(sender.sendMessage.bind(sender), textsecure.replay.Type.REBUILD_MESSAGE); this.sendRequestGroupSyncMessage = sender.sendRequestGroupSyncMessage .bind(sender); this.sendRequestContactSyncMessage = sender.sendRequestContactSyncMessage.bind(sender); diff --git a/js/models/messages.js b/js/models/messages.js index d016a9d5..9dcfd381 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -212,7 +212,8 @@ removeOutgoingErrors: function(number) { var errors = _.partition(this.get('errors'), function(e) { return e.number === number && - (e.name === 'OutgoingMessageError' || + (e.name === 'MessageError' || + e.name === 'OutgoingMessageError' || e.name === 'SendMessageNetworkError'); }); this.set({errors: errors[1]}); diff --git a/js/views/message_detail_view.js b/js/views/message_detail_view.js index b008d2ba..6cf8355c 100644 --- a/js/views/message_detail_view.js +++ b/js/views/message_detail_view.js @@ -86,7 +86,8 @@ }, retryMessage: function() { var retrys = _.filter(this.model.get('errors'), function(e) { - return (e.name === 'OutgoingMessageError' || + return (e.name === 'MessageError' || + e.name === 'OutgoingMessageError' || e.name === 'SendMessageNetworkError'); }); _.map(retrys, 'number').forEach(function(number) { @@ -105,7 +106,8 @@ render: function() { this.errors = _.groupBy(this.model.get('errors'), 'number'); var hasRetry = _.find(this.model.get('errors'), function(e) { - return (e.name === 'OutgoingMessageError' || + return (e.name === 'MessageError' || + e.name === 'OutgoingMessageError' || e.name === 'SendMessageNetworkError'); }); this.$el.html(Mustache.render(_.result(this, 'template', ''), { diff --git a/libtextsecure/errors.js b/libtextsecure/errors.js index f3521151..27a0eebc 100644 --- a/libtextsecure/errors.js +++ b/libtextsecure/errors.js @@ -9,6 +9,7 @@ ENCRYPT_MESSAGE: 1, INIT_SESSION: 2, TRANSMIT_MESSAGE: 3, + REBUILD_MESSAGE: 4, }; window.textsecure = window.textsecure || {}; window.textsecure.replay = { @@ -87,10 +88,25 @@ SendMessageNetworkError.prototype = new ReplayableError(); SendMessageNetworkError.prototype.constructor = SendMessageNetworkError; + function MessageError(message, httpError) { + ReplayableError.call(this, { + functionCode : Type.REBUILD_MESSAGE, + args : [message] + }); + this.name = 'MessageError'; + this.code = httpError.code; + this.message = httpError.message; + this.stack = httpError.stack; + } + MessageError.prototype = new ReplayableError(); + MessageError.prototype.constructor = MessageError; + + window.textsecure.SendMessageNetworkError = SendMessageNetworkError; window.textsecure.IncomingIdentityKeyError = IncomingIdentityKeyError; window.textsecure.OutgoingIdentityKeyError = OutgoingIdentityKeyError; window.textsecure.ReplayableError = ReplayableError; window.textsecure.OutgoingMessageError = OutgoingMessageError; + window.textsecure.MessageError = MessageError; })(); diff --git a/libtextsecure/sendmessage.js b/libtextsecure/sendmessage.js index eb80075e..1b30c096 100644 --- a/libtextsecure/sendmessage.js +++ b/libtextsecure/sendmessage.js @@ -11,6 +11,7 @@ function Message(options) { this.timestamp = options.timestamp; this.needsSync = options.needsSync; } + Message.prototype = { constructor: Message, toProto: function() { @@ -81,7 +82,8 @@ MessageSender.prototype = { }.bind(this)); }, - sendMessage: function(message) { + sendMessage: function(attrs) { + var message = new Message(attrs); return Promise.all( message.attachments.map(this.makeAttachmentPointer.bind(this)) ).then(function(attachmentPointers) { @@ -101,7 +103,13 @@ MessageSender.prototype = { } ); }.bind(this)); - }.bind(this)); + }.bind(this)).catch(function(error) { + if (error instanceof Error && error.name === 'HTTPError') { + throw new textsecure.MessageError(attrs, error); + } else { + throw error; + } + }); }, sendMessageProto: function(timestamp, numbers, message, callback) { var outgoing = new OutgoingMessage(this.server, timestamp, numbers, message, callback); @@ -195,15 +203,13 @@ MessageSender.prototype = { }, sendMessageToNumber: function(number, messageText, attachments, timestamp) { - var message = new Message({ + return this.sendMessage({ recipients : [number], body : messageText, timestamp : timestamp, attachments : attachments, needsSync : true }); - - return this.sendMessage(message); }, closeSession: function(number, timestamp) { @@ -234,7 +240,7 @@ MessageSender.prototype = { return Promise.reject(new Error('No other members in the group')); } - var message = new Message({ + return this.sendMessage({ recipients : numbers, body : messageText, timestamp : timestamp, @@ -245,7 +251,6 @@ MessageSender.prototype = { type: textsecure.protobuf.GroupContext.Type.DELIVER } }); - return this.sendMessage(message); }.bind(this)); }, @@ -364,6 +369,7 @@ textsecure.MessageSender = function(url, username, password, attachment_server_u 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.retransmitMessage.bind(sender), textsecure.replay.Type.TRANSMIT_MESSAGE); + textsecure.replay.registerFunction(sender.sendMessage.bind(sender), textsecure.replay.Type.REBUILD_MESSAGE); this.sendRequestGroupSyncMessage = sender.sendRequestGroupSyncMessage .bind(sender); this.sendRequestContactSyncMessage = sender.sendRequestContactSyncMessage.bind(sender);