Refactor MessageReceiver for storage/server independence

Let the libtextsecure consumer pass in their own server url, username,
password, and signaling key, as with libtextsecure-java.

Also brings reconnect logic up into the MessageReceiver class, which
is the only place it should apply.
pull/749/head
lilia 10 years ago
parent e59a5792d5
commit a925027cd2

@ -65,7 +65,13 @@
if (!textsecure.registration.isDone()) { return; } if (!textsecure.registration.isDone()) { return; }
// initialize the socket and start listening for messages // initialize the socket and start listening for messages
messageReceiver = new textsecure.MessageReceiver('wss://textsecure-service-staging.whispersystems.org', window); messageReceiver = new textsecure.MessageReceiver(
'https://textsecure-service-staging.whispersystems.org',
textsecure.storage.get('number_id'),
textsecure.storage.get('password'),
textsecure.storage.get('signaling_key'),
window
);
} }
function onContactReceived(ev) { function onContactReceived(ev) {

@ -39381,10 +39381,17 @@ function generateKeys(count, progressCallback) {
'use strict'; 'use strict';
window.textsecure = window.textsecure || {}; window.textsecure = window.textsecure || {};
function MessageReceiver(url, eventTarget) { function MessageReceiver(url, username, password, signalingKey, eventTarget) {
this.url = url;
if (eventTarget instanceof EventTarget) { if (eventTarget instanceof EventTarget) {
this.target = eventTarget; this.target = eventTarget;
this.url = url;
this.signalingKey = signalingKey;
this.username = username;
this.password = password;
var unencoded = textsecure.utils.unencodeNumber(username);
this.number = unencoded[0];
this.deviceId = unencoded[1];
} else { } else {
throw new TypeError('MessageReceiver expected an EventTarget'); throw new TypeError('MessageReceiver expected an EventTarget');
} }
@ -39394,31 +39401,44 @@ function generateKeys(count, progressCallback) {
MessageReceiver.prototype = { MessageReceiver.prototype = {
constructor: MessageReceiver, constructor: MessageReceiver,
connect: function() { connect: function() {
var eventTarget = this.target;
// initialize the socket and start listening for messages // initialize the socket and start listening for messages
this.socket = TextSecureServer.getMessageWebsocket(this.url); if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {
this.socket.onclose = function(e) { socket.close();
// possible 403. Make a request to confirm }
TextSecureServer.getDevices(textsecure.storage.user.getNumber()). this.socket = new WebSocket(
then(this.connect.bind(this)). this.url.replace('https://', 'wss://').replace('http://', 'ws://')
catch(function(e) { + '/v1/websocket/?login=' + encodeURIComponent(this.username)
var ev = new Event('textsecure:error'); + '&password=' + encodeURIComponent(this.password)
ev.error = e; );
eventTarget.dispatchEvent(ev);
});
}.bind(this);
this.socket.onclose = this.onclose.bind(this);
this.socket.onerror = this.onerror.bind(this);
this.wsr = new WebSocketResource(this.socket, { this.wsr = new WebSocketResource(this.socket, {
handleRequest: this.handleRequest.bind(this), handleRequest: this.handleRequest.bind(this),
keepalive: { path: '/v1/keepalive', disconnect: true } keepalive: { path: '/v1/keepalive', disconnect: true }
}); });
},
onerror: function(error) {
console.log('websocket error', error);
this.socketError = error;
},
onclose: function(ev) {
var eventTarget = this.target;
console.log('websocket closed', ev.code);
// possible 403 or network issue. Make an request to confirm
TextSecureServer(this.url).getDevices(this.number).
then(this.connect.bind(this)). // No HTTP error? Reconnect
catch(function(e) {
var ev = new Event('textsecure:error');
ev.error = e;
eventTarget.dispatchEvent(ev);
});
}, },
handleRequest: function(request) { handleRequest: function(request) {
this.wsr.resetKeepAliveTimer(); this.wsr.resetKeepAliveTimer();
// TODO: handle different types of requests. for now we only expect // TODO: handle different types of requests. for now we only expect
// PUT /messages <encrypted IncomingPushMessageSignal> // PUT /messages <encrypted IncomingPushMessageSignal>
textsecure.crypto.decryptWebsocketMessage(request.body).then(function(plaintext) { textsecure.crypto.decryptWebsocketMessage(request.body, this.signalingKey).then(function(plaintext) {
var envelope = textsecure.protobuf.Envelope.decode(plaintext); var envelope = textsecure.protobuf.Envelope.decode(plaintext);
// After this point, decoding errors are not the server's // After this point, decoding errors are not the server's
// fault, and we should handle them gracefully and tell the // fault, and we should handle them gracefully and tell the
@ -39445,7 +39465,7 @@ function generateKeys(count, progressCallback) {
}, },
getStatus: function() { getStatus: function() {
if (this.socket) { if (this.socket) {
return this.socket.getStatus(); return this.socket.readyState;
} else { } else {
return -1; return -1;
} }
@ -39470,8 +39490,7 @@ function generateKeys(count, progressCallback) {
}.bind(this)); }.bind(this));
}, },
handleSentMessage: function(destination, timestamp, message) { handleSentMessage: function(destination, timestamp, message) {
var source = textsecure.storage.user.getNumber(); return processDecrypted(message, this.number).then(function(message) {
return processDecrypted(message, source).then(function(message) {
var ev = new Event('textsecure:sent'); var ev = new Event('textsecure:sent');
ev.data = { ev.data = {
destination : destination, destination : destination,
@ -39519,10 +39538,10 @@ function generateKeys(count, progressCallback) {
}.bind(this)); }.bind(this));
}, },
handleSyncMessage: function(envelope, syncMessage) { handleSyncMessage: function(envelope, syncMessage) {
if (envelope.source !== textsecure.storage.user.getNumber()) { if (envelope.source !== this.number) {
throw new Error('Received sync message from another number'); throw new Error('Received sync message from another number');
} }
if (envelope.sourceDevice == textsecure.storage.user.getDeviceId()) { if (envelope.sourceDevice == this.deviceId) {
throw new Error('Received sync message from our own device'); throw new Error('Received sync message from our own device');
} }
if (syncMessage.sent) { if (syncMessage.sent) {
@ -39587,8 +39606,8 @@ function generateKeys(count, progressCallback) {
} }
}; };
textsecure.MessageReceiver = function (url, eventTarget) { textsecure.MessageReceiver = function(url, username, password, signalingKey, eventTarget) {
var messageReceiver = new MessageReceiver(url, eventTarget); var messageReceiver = new MessageReceiver(url, username, password, signalingKey, eventTarget);
this.getStatus = function() { this.getStatus = function() {
return messageReceiver.getStatus(); return messageReceiver.getStatus();
} }

@ -6,10 +6,17 @@
'use strict'; 'use strict';
window.textsecure = window.textsecure || {}; window.textsecure = window.textsecure || {};
function MessageReceiver(url, eventTarget) { function MessageReceiver(url, username, password, signalingKey, eventTarget) {
this.url = url;
if (eventTarget instanceof EventTarget) { if (eventTarget instanceof EventTarget) {
this.target = eventTarget; this.target = eventTarget;
this.url = url;
this.signalingKey = signalingKey;
this.username = username;
this.password = password;
var unencoded = textsecure.utils.unencodeNumber(username);
this.number = unencoded[0];
this.deviceId = unencoded[1];
} else { } else {
throw new TypeError('MessageReceiver expected an EventTarget'); throw new TypeError('MessageReceiver expected an EventTarget');
} }
@ -19,31 +26,44 @@
MessageReceiver.prototype = { MessageReceiver.prototype = {
constructor: MessageReceiver, constructor: MessageReceiver,
connect: function() { connect: function() {
var eventTarget = this.target;
// initialize the socket and start listening for messages // initialize the socket and start listening for messages
this.socket = TextSecureServer.getMessageWebsocket(this.url); if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {
this.socket.onclose = function(e) { socket.close();
// possible 403. Make a request to confirm }
TextSecureServer.getDevices(textsecure.storage.user.getNumber()). this.socket = new WebSocket(
then(this.connect.bind(this)). this.url.replace('https://', 'wss://').replace('http://', 'ws://')
catch(function(e) { + '/v1/websocket/?login=' + encodeURIComponent(this.username)
var ev = new Event('textsecure:error'); + '&password=' + encodeURIComponent(this.password)
ev.error = e; );
eventTarget.dispatchEvent(ev);
});
}.bind(this);
this.socket.onclose = this.onclose.bind(this);
this.socket.onerror = this.onerror.bind(this);
this.wsr = new WebSocketResource(this.socket, { this.wsr = new WebSocketResource(this.socket, {
handleRequest: this.handleRequest.bind(this), handleRequest: this.handleRequest.bind(this),
keepalive: { path: '/v1/keepalive', disconnect: true } keepalive: { path: '/v1/keepalive', disconnect: true }
}); });
},
onerror: function(error) {
console.log('websocket error', error);
this.socketError = error;
},
onclose: function(ev) {
var eventTarget = this.target;
console.log('websocket closed', ev.code);
// possible 403 or network issue. Make an request to confirm
TextSecureServer(this.url).getDevices(this.number).
then(this.connect.bind(this)). // No HTTP error? Reconnect
catch(function(e) {
var ev = new Event('textsecure:error');
ev.error = e;
eventTarget.dispatchEvent(ev);
});
}, },
handleRequest: function(request) { handleRequest: function(request) {
this.wsr.resetKeepAliveTimer(); this.wsr.resetKeepAliveTimer();
// TODO: handle different types of requests. for now we only expect // TODO: handle different types of requests. for now we only expect
// PUT /messages <encrypted IncomingPushMessageSignal> // PUT /messages <encrypted IncomingPushMessageSignal>
textsecure.crypto.decryptWebsocketMessage(request.body).then(function(plaintext) { textsecure.crypto.decryptWebsocketMessage(request.body, this.signalingKey).then(function(plaintext) {
var envelope = textsecure.protobuf.Envelope.decode(plaintext); var envelope = textsecure.protobuf.Envelope.decode(plaintext);
// After this point, decoding errors are not the server's // After this point, decoding errors are not the server's
// fault, and we should handle them gracefully and tell the // fault, and we should handle them gracefully and tell the
@ -70,7 +90,7 @@
}, },
getStatus: function() { getStatus: function() {
if (this.socket) { if (this.socket) {
return this.socket.getStatus(); return this.socket.readyState;
} else { } else {
return -1; return -1;
} }
@ -95,8 +115,7 @@
}.bind(this)); }.bind(this));
}, },
handleSentMessage: function(destination, timestamp, message) { handleSentMessage: function(destination, timestamp, message) {
var source = textsecure.storage.user.getNumber(); return processDecrypted(message, this.number).then(function(message) {
return processDecrypted(message, source).then(function(message) {
var ev = new Event('textsecure:sent'); var ev = new Event('textsecure:sent');
ev.data = { ev.data = {
destination : destination, destination : destination,
@ -144,10 +163,10 @@
}.bind(this)); }.bind(this));
}, },
handleSyncMessage: function(envelope, syncMessage) { handleSyncMessage: function(envelope, syncMessage) {
if (envelope.source !== textsecure.storage.user.getNumber()) { if (envelope.source !== this.number) {
throw new Error('Received sync message from another number'); throw new Error('Received sync message from another number');
} }
if (envelope.sourceDevice == textsecure.storage.user.getDeviceId()) { if (envelope.sourceDevice == this.deviceId) {
throw new Error('Received sync message from our own device'); throw new Error('Received sync message from our own device');
} }
if (syncMessage.sent) { if (syncMessage.sent) {
@ -212,8 +231,8 @@
} }
}; };
textsecure.MessageReceiver = function (url, eventTarget) { textsecure.MessageReceiver = function(url, username, password, signalingKey, eventTarget) {
var messageReceiver = new MessageReceiver(url, eventTarget); var messageReceiver = new MessageReceiver(url, username, password, signalingKey, eventTarget);
this.getStatus = function() { this.getStatus = function() {
return messageReceiver.getStatus(); return messageReceiver.getStatus();
} }

Loading…
Cancel
Save