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.
This commit is contained in:
lilia 2015-08-07 17:04:49 -07:00
parent e59a5792d5
commit a925027cd2
3 changed files with 91 additions and 47 deletions

View file

@ -65,7 +65,13 @@
if (!textsecure.registration.isDone()) { return; }
// 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) {

View file

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

View file

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