diff --git a/js/api.js b/js/api.js
index 2d66e93d57..5773a76c47 100644
--- a/js/api.js
+++ b/js/api.js
@@ -14,24 +14,27 @@
* along with this program. If not, see .
*/
-/************************************************
- *** Utilities to communicate with the server ***
- ************************************************/
-// WARNING: THIS SERVER LOGS KEY MATERIAL FOR TESTING
-var URL_BASE = "http://sushiforeveryone.bluematt.me";
+window.textsecure = window.textsecure || {};
-// This is the real server
-//var URL_BASE = "https://textsecure-service.whispersystems.org";
+window.textsecure.api = function() {
+ var self = {};
-var URL_CALLS = {};
-URL_CALLS['accounts'] = "/v1/accounts";
-URL_CALLS['devices'] = "/v1/devices";
-URL_CALLS['keys'] = "/v1/keys";
-URL_CALLS['push'] = "/v1/websocket";
-URL_CALLS['messages'] = "/v1/messages";
-URL_CALLS['attachment'] = "/v1/attachments";
+ /************************************************
+ *** Utilities to communicate with the server ***
+ ************************************************/
+ // WARNING: THIS SERVER LOGS KEY MATERIAL FOR TESTING
+ var URL_BASE = "http://sushiforeveryone.bluematt.me";
-var API = new function() {
+ // This is the real server
+ //var URL_BASE = "https://textsecure-service.whispersystems.org";
+
+ var URL_CALLS = {};
+ URL_CALLS['accounts'] = "/v1/accounts";
+ URL_CALLS['devices'] = "/v1/devices";
+ URL_CALLS['keys'] = "/v1/keys";
+ URL_CALLS['push'] = "/v1/websocket";
+ URL_CALLS['messages'] = "/v1/messages";
+ URL_CALLS['attachment'] = "/v1/attachments";
/**
* REQUIRED PARAMS:
@@ -90,7 +93,7 @@ var API = new function() {
});
};
- this.requestVerificationCode = function(number, success_callback, error_callback) {
+ self.requestVerificationCode = function(number, success_callback, error_callback) {
doAjax({
call : 'accounts',
httpType : 'GET',
@@ -104,7 +107,7 @@ var API = new function() {
});
};
- this.confirmCode = function(code, number, password,
+ self.confirmCode = function(code, number, password,
signaling_key, registrationId, single_device,
success_callback, error_callback) {
var call = single_device ? 'accounts' : 'devices';
@@ -129,7 +132,7 @@ var API = new function() {
});
};
- this.registerKeys = function(keys, success_callback, error_callback) {
+ self.registerKeys = function(keys, success_callback, error_callback) {
//TODO: Do this conversion somewhere else?
var identityKey = btoa(getString(keys.keys[0].identityKey));
for (var i = 0; i < keys.keys.length; i++)
@@ -149,7 +152,7 @@ var API = new function() {
});
};
- this.getKeysForNumber = function(number) {
+ self.getKeysForNumber = function(number) {
return doAjax({
call : 'keys',
httpType : 'GET',
@@ -168,7 +171,7 @@ var API = new function() {
});
};
- this.sendMessages = function(destination, messageArray) {
+ self.sendMessages = function(destination, messageArray) {
//TODO: Do this conversion somewhere else?
for (var i = 0; i < messageArray.length; i++)
messageArray[i].body = btoa(messageArray[i].body);
@@ -185,7 +188,7 @@ var API = new function() {
});
};
- this.getAttachment = function(id) {
+ self.getAttachment = function(id) {
return doAjax({
call : 'attachment',
httpType : 'GET',
@@ -218,4 +221,6 @@ var API = new function() {
});
});
};
-}(); // API
+
+ return self;
+}();
diff --git a/js/background.js b/js/background.js
index 8f659c9c9d..06e38db5ed 100644
--- a/js/background.js
+++ b/js/background.js
@@ -14,13 +14,13 @@
* along with this program. If not, see .
*/
-registerOnLoadFunction(function() {
+textsecure.registerOnLoadFunction(function() {
if (!localStorage.getItem('first_install_ran')) {
localStorage.setItem('first_install_ran', 1);
chrome.tabs.create({url: "options.html"});
} else {
if (isRegistrationDone()) {
- subscribeToPush(function(message) {
+ textsecure.subscribeToPush(function(message) {
console.log("Got message from " + message.pushMessage.source + "." + message.pushMessage.sourceDevice +
': "' + getString(message.message.body) + '"');
var newUnreadCount = storage.getUnencrypted("unreadCount", 0) + 1;
diff --git a/js/crypto.js b/js/crypto.js
index 8fead8a8f0..5a70a10835 100644
--- a/js/crypto.js
+++ b/js/crypto.js
@@ -14,9 +14,10 @@
* along with this program. If not, see .
*/
-var textsecure = textsecure || {};
+window.textsecure = window.textsecure || {};
-textsecure.crypto = new function() {
+window.textsecure.crypto = new function() {
+ var self = {};
// functions exposed for replacement and direct calling in test code
var testing_only = {};
@@ -38,7 +39,7 @@ textsecure.crypto = new function() {
throw err;
}
}
- this.getRandomBytes = getRandomBytes;
+ self.getRandomBytes = getRandomBytes;
function intToArrayBuffer(nInt) {
var res = new ArrayBuffer(16);
@@ -67,7 +68,7 @@ textsecure.crypto = new function() {
return pub;
}
- if (USE_NACL) {
+ if (textsecure.nacl.USE_NACL) {
return postNaclMessage({command: "bytesToPriv", priv: privKey}).then(function(message) {
var priv = message.res;
if (!isIdentity)
@@ -238,7 +239,7 @@ textsecure.crypto = new function() {
console.error("WARNING: Expected pubkey of length 33, please report the ST and client that generated the pubkey");
return new Promise(function(resolve) {
- if (USE_NACL) {
+ if (textsecure.nacl.USE_NACL) {
postNaclMessage({command: "ECDHE", priv: privKey, pub: pubKey}).then(function(message) {
resolve(message.res);
});
@@ -556,7 +557,7 @@ textsecure.crypto = new function() {
*** Public crypto API ***
*************************/
// Decrypts message into a raw string
- this.decryptWebsocketMessage = function(message) {
+ self.decryptWebsocketMessage = function(message) {
var signaling_key = storage.getEncrypted("signaling_key"); //TODO: in crypto_storage
var aes_key = toArrayBuffer(signaling_key.substring(0, 32));
var mac_key = toArrayBuffer(signaling_key.substring(32, 32 + 20));
@@ -575,7 +576,7 @@ textsecure.crypto = new function() {
});
};
- this.decryptAttachment = function(encryptedBin, keys) {
+ self.decryptAttachment = function(encryptedBin, keys) {
var aes_key = keys.slice(0, 32);
var mac_key = keys.slice(32, 64);
@@ -589,7 +590,7 @@ textsecure.crypto = new function() {
});
};
- this.handleIncomingPushMessageProto = function(proto) {
+ self.handleIncomingPushMessageProto = function(proto) {
switch(proto.type) {
case 0: //TYPE_MESSAGE_PLAINTEXT
return Promise.resolve({message: decodePushMessageContentProtobuf(getString(proto.message)), pushMessage:proto});
@@ -612,7 +613,7 @@ textsecure.crypto = new function() {
}
// return Promise(encoded [PreKey]WhisperMessage)
- this.encryptMessageFor = function(deviceObject, pushMessageContent) {
+ self.encryptMessageFor = function(deviceObject, pushMessageContent) {
var session = crypto_storage.getOpenSession(deviceObject.encodedNumber);
var doEncryptPushMessageContent = function() {
@@ -679,7 +680,7 @@ textsecure.crypto = new function() {
}
var GENERATE_KEYS_KEYS_GENERATED = 100;
- this.generateKeys = function() {
+ self.generateKeys = function() {
var identityKey = crypto_storage.getStoredPubKey("identityKey");
var identityKeyCalculated = function(pubKey) {
identityKey = pubKey;
@@ -721,5 +722,6 @@ textsecure.crypto = new function() {
return identityKeyCalculated(identityKey);
}
- this.testing_only = testing_only;
+ self.testing_only = testing_only;
+ return self;
}();
diff --git a/js/fake_api.js b/js/fake_api.js
index 61691b4320..182cf8ce7d 100644
--- a/js/fake_api.js
+++ b/js/fake_api.js
@@ -14,6 +14,7 @@
* along with this program. If not, see .
*/
+//TODO: Redo this (API has changed to textsecure.api and changed)
var FakeWhisperAPI = function() {
var doAjax = function(param) {
if (param.success_callback) {
diff --git a/js/helpers.js b/js/helpers.js
index 8d6f5a68a7..584b194888 100644
--- a/js/helpers.js
+++ b/js/helpers.js
@@ -92,6 +92,8 @@ function base64EncArr (aBytes) {
/* END CRAP TO BE DELETED */
+window.textsecure = window.textsecure || {};
+
/*********************************
*** Type conversion utilities ***
*********************************/
@@ -176,7 +178,7 @@ function base64ToArrayBuffer(string) {
return base64DecToArr(string);
}
-// Protobuf decodingA
+// Protobuf decoding
//TODO: throw on missing fields everywhere
var IncomingPushMessageProtobuf = dcodeIO.ProtoBuf.loadProtoFile("protos/IncomingPushMessageSignal.proto").build("textsecure.IncomingPushMessageSignal");
function decodeIncomingPushMessageProtobuf(string) {
@@ -224,13 +226,6 @@ function verifyNumber(string) {
return getEncodedNumber(string.trim());
}
-function getDeviceId(encodedNumber) {
- var split = encodedNumber.split(".");
- if (split.length > 1)
- return split[1];
- return 1;
-}
-
// Other
function timestampToHumanReadable(timestamp) {
@@ -251,18 +246,21 @@ function objectContainsKeys(object) {
/************************************************
*** Utilities to store data in local storage ***
************************************************/
-var storage = new function() {
+//TODO: textsecure.storage
+window.storage = function() {
+ var self = {};
+
/*****************************
*** Base Storage Routines ***
*****************************/
- this.putEncrypted = function(key, value) {
+ self.putEncrypted = function(key, value) {
//TODO
if (value === undefined)
throw new Error("Tried to store undefined");
localStorage.setItem("e" + key, jsonThing(value));
}
- this.getEncrypted = function(key, defaultValue) {
+ self.getEncrypted = function(key, defaultValue) {
//TODO
var value = localStorage.getItem("e" + key);
if (value === null)
@@ -270,40 +268,42 @@ var storage = new function() {
return JSON.parse(value);
}
- this.removeEncrypted = function(key) {
+ self.removeEncrypted = function(key) {
localStorage.removeItem("e" + key);
}
- this.putUnencrypted = function(key, value) {
+ self.putUnencrypted = function(key, value) {
if (value === undefined)
throw new Error("Tried to store undefined");
localStorage.setItem("u" + key, jsonThing(value));
}
- this.getUnencrypted = function(key, defaultValue) {
+ self.getUnencrypted = function(key, defaultValue) {
var value = localStorage.getItem("u" + key);
if (value === null)
return defaultValue;
return JSON.parse(value);
}
- this.removeUnencrypted = function(key) {
+ self.removeUnencrypted = function(key) {
localStorage.removeItem("u" + key);
}
/**********************
*** Device Storage ***
**********************/
- this.devices = new function() {
- this.getDeviceObject = function(encodedNumber) {
+ self.devices = function() {
+ var self = {};
+
+ self.getDeviceObject = function(encodedNumber) {
return storage.getEncrypted("deviceObject" + getEncodedNumber(encodedNumber));
}
- this.getDeviceIdListFromNumber = function(number) {
+ self.getDeviceIdListFromNumber = function(number) {
return storage.getEncrypted("deviceIdList" + getNumberFromString(number), []);
}
- this.addDeviceIdForNumber = function(number, deviceId) {
+ self.addDeviceIdForNumber = function(number, deviceId) {
var deviceIdList = this.getDeviceIdListFromNumber(getNumberFromString(number));
for (var i = 0; i < deviceIdList.length; i++) {
if (deviceIdList[i] == deviceId)
@@ -313,8 +313,15 @@ var storage = new function() {
storage.putEncrypted("deviceIdList" + getNumberFromString(number), deviceIdList);
}
+ var getDeviceId = function(encodedNumber) {
+ var split = encodedNumber.split(".");
+ if (split.length > 1)
+ return split[1];
+ return 1;
+ }
+
// throws "Identity key mismatch"
- this.saveDeviceObject = function(deviceObject) {
+ self.saveDeviceObject = function(deviceObject) {
var existing = this.getDeviceObject(deviceObject.encodedNumber);
if (existing === undefined)
existing = {encodedNumber: getEncodedNumber(deviceObject.encodedNumber)};
@@ -331,15 +338,19 @@ var storage = new function() {
this.addDeviceIdForNumber(deviceObject.encodedNumber, getDeviceId(deviceObject.encodedNumber));
}
- this.getDeviceObjectListFromNumber = function(number) {
+ self.getDeviceObjectListFromNumber = function(number) {
var deviceObjectList = [];
var deviceIdList = this.getDeviceIdListFromNumber(number);
for (var i = 0; i < deviceIdList.length; i++)
deviceObjectList[deviceObjectList.length] = this.getDeviceObject(getNumberFromString(number) + "." + deviceIdList[i]);
return deviceObjectList;
}
- };
-};
+
+ return self;
+ }();
+
+ return self;
+}();
function registrationDone() {
storage.putUnencrypted("registration_done", "");
@@ -374,221 +385,232 @@ function storeMessage(messageObject) {
/**********************
*** NaCL Interface ***
**********************/
-var USE_NACL = false;
+window.textsecure.nacl = function() {
+ var self = {};
-var onLoadCallbacks = [];
-var naclLoaded = 0;
-function registerOnLoadFunction(func) {
- if (naclLoaded || !USE_NACL) {
- func();
- return;
- }
- onLoadCallbacks[onLoadCallbacks.length] = func;
-}
+ self.USE_NACL = false;
-var naclMessageNextId = 0;
-var naclMessageIdCallbackMap = {};
-function moduleDidLoad() {
- common.hideModule();
- naclLoaded = 1;
- for (var i = 0; i < onLoadCallbacks.length; i++)
- onLoadCallbacks[i]();
- onLoadCallbacks = [];
-}
-
-function handleMessage(message) {
- naclMessageIdCallbackMap[message.data.call_id](message.data);
-}
-
-function postNaclMessage(message) {
- if (!USE_NACL)
- throw new Error("Attempted to make NaCL call with !USE_NACL?");
-
- return new Promise(function(resolve) {
- naclMessageIdCallbackMap[naclMessageNextId] = resolve;
- message.call_id = naclMessageNextId++;
-
- common.naclModule.postMessage(message);
- });
-}
-
-// message_callback(decoded_protobuf) (use decodeMessage(proto))
-var subscribeToPushMessageSemaphore = 0;
-function subscribeToPush(message_callback) {
- subscribeToPushMessageSemaphore++;
- if (subscribeToPushMessageSemaphore <= 0)
- return;
-
- var user = storage.getUnencrypted("number_id");
- var password = storage.getEncrypted("password");
- var URL = URL_BASE.replace(/^http:/g, "ws:").replace(/^https:/g, "wss:") + URL_CALLS['push'] + "/?user=%2B" + getString(user).substring(1) + "&password=" + getString(password);
- var socket = new WebSocket(URL);
-
- var pingInterval;
-
- //TODO: GUI
- socket.onerror = function(socketEvent) {
- console.log('Server is down :(');
- clearInterval(pingInterval);
- subscribeToPushMessageSemaphore--;
- setTimeout(function() { subscribeToPush(message_callback); }, 60000);
- };
- socket.onclose = function(socketEvent) {
- console.log('Server closed :(');
- clearInterval(pingInterval);
- subscribeToPushMessageSemaphore--;
- setTimeout(function() { subscribeToPush(message_callback); }, 60000);
- };
- socket.onopen = function(socketEvent) {
- console.log('Connected to server!');
- pingInterval = setInterval(function() { console.log("Sending server ping message."); socket.send(JSON.stringify({type: 2})); }, 30000);
- };
-
- socket.onmessage = function(response) {
- try {
- var message = JSON.parse(response.data);
- } catch (e) {
- console.log('Error parsing server JSON message: ' + response.responseBody.split("|")[1]);
+ var onLoadCallbacks = [];
+ var naclLoaded = 0;
+ self.registerOnLoadFunction = function(func) {
+ if (naclLoaded || !self.USE_NACL) {
+ func();
return;
}
+ onLoadCallbacks[onLoadCallbacks.length] = func;
+ }
- if (message.type == 3) {
- console.log("Got pong message");
- } else if (message.type === undefined && message.id !== undefined) {
- textsecure.crypto.decryptWebsocketMessage(message.message).then(function(plaintext) {
- var proto = decodeIncomingPushMessageProtobuf(getString(plaintext));
- // After this point, a) decoding errors are not the server's fault, and
- // b) we should handle them gracefully and tell the user they received an invalid message
- console.log("Successfully decoded message with id: " + message.id);
- socket.send(JSON.stringify({type: 1, id: message.id}));
- return textsecure.crypto.handleIncomingPushMessageProto(proto).then(function(decrypted) {
- var handleAttachment = function(attachment) {
- return API.getAttachment(attachment.id).then(function(encryptedBin) {
- return textsecure.crypto.decryptAttachment(encryptedBin, toArrayBuffer(attachment.key)).then(function(decryptedBin) {
- attachment.decrypted = decryptedBin;
+ var naclMessageNextId = 0;
+ var naclMessageIdCallbackMap = {};
+ window.moduleDidLoad = function() {
+ common.hideModule();
+ naclLoaded = 1;
+ for (var i = 0; i < onLoadCallbacks.length; i++)
+ onLoadCallbacks[i]();
+ onLoadCallbacks = [];
+ }
+
+ window.handleMessage = function(message) {
+ naclMessageIdCallbackMap[message.data.call_id](message.data);
+ }
+
+ self.postNaclMessage = function(message) {
+ if (!self.USE_NACL)
+ throw new Error("Attempted to make NaCL call with !USE_NACL?");
+
+ return new Promise(function(resolve) {
+ naclMessageIdCallbackMap[naclMessageNextId] = resolve;
+ message.call_id = naclMessageNextId++;
+
+ common.naclModule.postMessage(message);
+ });
+ }
+
+ return self;
+}();
+
+//TODO: Some kind of textsecure.init(use_nacl)
+window.textsecure.registerOnLoadFunction = window.textsecure.nacl.registerOnLoadFunction;
+
+// message_callback({message: decryptedMessage, pushMessage: server-providedPushMessage})
+window.textsecure.subscribeToPush = function() {
+ var subscribeToPushMessageSemaphore = 0;
+ return function(message_callback) {
+ subscribeToPushMessageSemaphore++;
+ if (subscribeToPushMessageSemaphore <= 0)
+ return;
+
+ var user = storage.getUnencrypted("number_id");
+ var password = storage.getEncrypted("password");
+ var URL = URL_BASE.replace(/^http:/g, "ws:").replace(/^https:/g, "wss:") + URL_CALLS['push'] + "/?user=%2B" + getString(user).substring(1) + "&password=" + getString(password);
+ var socket = new WebSocket(URL);
+
+ var pingInterval;
+
+ //TODO: GUI
+ socket.onerror = function(socketEvent) {
+ console.log('Server is down :(');
+ clearInterval(pingInterval);
+ subscribeToPushMessageSemaphore--;
+ setTimeout(function() { subscribeToPush(message_callback); }, 60000);
+ };
+ socket.onclose = function(socketEvent) {
+ console.log('Server closed :(');
+ clearInterval(pingInterval);
+ subscribeToPushMessageSemaphore--;
+ setTimeout(function() { subscribeToPush(message_callback); }, 60000);
+ };
+ socket.onopen = function(socketEvent) {
+ console.log('Connected to server!');
+ pingInterval = setInterval(function() { console.log("Sending server ping message."); socket.send(JSON.stringify({type: 2})); }, 30000);
+ };
+
+ socket.onmessage = function(response) {
+ try {
+ var message = JSON.parse(response.data);
+ } catch (e) {
+ console.log('Error parsing server JSON message: ' + response.responseBody.split("|")[1]);
+ return;
+ }
+
+ if (message.type == 3) {
+ console.log("Got pong message");
+ } else if (message.type === undefined && message.id !== undefined) {
+ textsecure.crypto.decryptWebsocketMessage(message.message).then(function(plaintext) {
+ var proto = decodeIncomingPushMessageProtobuf(getString(plaintext));
+ // After this point, a) decoding errors are not the server's fault, and
+ // b) we should handle them gracefully and tell the user they received an invalid message
+ console.log("Successfully decoded message with id: " + message.id);
+ socket.send(JSON.stringify({type: 1, id: message.id}));
+ return textsecure.crypto.handleIncomingPushMessageProto(proto).then(function(decrypted) {
+ var handleAttachment = function(attachment) {
+ return textsecure.api.getAttachment(attachment.id).then(function(encryptedBin) {
+ return textsecure.crypto.decryptAttachment(encryptedBin, toArrayBuffer(attachment.key)).then(function(decryptedBin) {
+ attachment.decrypted = decryptedBin;
+ });
});
+ };
+
+ var promises = [];
+ for (var i = 0; i < decrypted.message.attachments.length; i++) {
+ promises[i] = handleAttachment(decrypted.message.attachments[i]);
+ }
+ return Promise.all(promises).then(function() {
+ storeMessage(decrypted);
+ message_callback(decrypted);
});
- };
-
- var promises = [];
- for (var i = 0; i < decrypted.message.attachments.length; i++) {
- promises[i] = handleAttachment(decrypted.message.attachments[i]);
- }
- return Promise.all(promises).then(function() {
- storeMessage(decrypted);
- message_callback(decrypted);
- });
- })
- }).catch(function(e) {
- console.log("Error handling incoming message: ");
- console.log(e);
- });
- }
- };
-}
-
-// success_callback(identity_key), error_callback(error_msg)
-function getKeysForNumber(number) {
- return API.getKeysForNumber(number).then(function(response) {
- for (var i = 0; i < response.length; i++) {
- storage.devices.saveDeviceObject({
- encodedNumber: number + "." + response[i].deviceId,
- identityKey: response[i].identityKey,
- publicKey: response[i].publicKey,
- preKeyId: response[i].keyId,
- registrationId: response[i].registrationId
- });
- }
- return response[0].identityKey;
- });
-}
-
-// success_callback(server success/failure map), error_callback(error_msg)
-// message == PushMessageContentProto (NOT STRING)
-function sendMessageToDevices(number, deviceObjectList, message, success_callback, error_callback) {
- var jsonData = [];
- var relay = undefined;
- var promises = [];
-
- var addEncryptionFor = function(i) {
- return textsecure.crypto.encryptMessageFor(deviceObjectList[i], message).then(function(encryptedMsg) {
- jsonData[i] = {
- type: encryptedMsg.type,
- destination: deviceObjectList[i].encodedNumber,
- destinationRegistrationId: deviceObjectList[i].registrationId,
- body: encryptedMsg.body,
- timestamp: new Date().getTime()
- };
-
- if (deviceObjectList[i].relay !== undefined) {
- jsonData[i].relay = deviceObjectList[i].relay;
- if (relay === undefined)
- relay = jsonData[i].relay;
- else if (relay != jsonData[i].relay)
- throw new Error("Mismatched relays for number " + number);
- } else {
- if (relay === undefined)
- relay = "";
- else if (relay != "")
- throw new Error("Mismatched relays for number " + number);
+ })
+ }).catch(function(e) {
+ console.log("Error handling incoming message: ");
+ console.log(e);
+ });
}
+ };
+ }
+}();
+
+// sendMessage(numbers = [], message = PushMessageContentProto, callback(success/failure map))
+window.textsecure.sendMessage = function() {
+ function getKeysForNumber(number) {
+ return textsecure.api.getKeysForNumber(number).then(function(response) {
+ for (var i = 0; i < response.length; i++) {
+ storage.devices.saveDeviceObject({
+ encodedNumber: number + "." + response[i].deviceId,
+ identityKey: response[i].identityKey,
+ publicKey: response[i].publicKey,
+ preKeyId: response[i].keyId,
+ registrationId: response[i].registrationId
+ });
+ }
+ return response[0].identityKey;
});
}
- for (var i = 0; i < deviceObjectList.length; i++)
- promises[i] = addEncryptionFor(i);
- return Promise.all(promises).then(function() {
- return API.sendMessages(number, jsonData);
- });
-}
-// callback(success/failure map, see code)
-// message == PushMessageContentProto (NOT STRING)
-function sendMessageToNumbers(numbers, message, callback) {
- var numbersCompleted = 0;
- var errors = [];
- var successfulNumbers = [];
+ // success_callback(server success/failure map), error_callback(error_msg)
+ // message == PushMessageContentProto (NOT STRING)
+ function sendMessageToDevices(number, deviceObjectList, message, success_callback, error_callback) {
+ var jsonData = [];
+ var relay = undefined;
+ var promises = [];
- var numberCompleted = function() {
- numbersCompleted++;
- if (numbersCompleted >= numbers.length)
- callback({success: successfulNumbers, failure: errors});
+ var addEncryptionFor = function(i) {
+ return textsecure.crypto.encryptMessageFor(deviceObjectList[i], message).then(function(encryptedMsg) {
+ jsonData[i] = {
+ type: encryptedMsg.type,
+ destination: deviceObjectList[i].encodedNumber,
+ destinationRegistrationId: deviceObjectList[i].registrationId,
+ body: encryptedMsg.body,
+ timestamp: new Date().getTime()
+ };
+
+ if (deviceObjectList[i].relay !== undefined) {
+ jsonData[i].relay = deviceObjectList[i].relay;
+ if (relay === undefined)
+ relay = jsonData[i].relay;
+ else if (relay != jsonData[i].relay)
+ throw new Error("Mismatched relays for number " + number);
+ } else {
+ if (relay === undefined)
+ relay = "";
+ else if (relay != "")
+ throw new Error("Mismatched relays for number " + number);
+ }
+ });
+ }
+ for (var i = 0; i < deviceObjectList.length; i++)
+ promises[i] = addEncryptionFor(i);
+ return Promise.all(promises).then(function() {
+ return textsecure.api.sendMessages(number, jsonData);
+ });
}
- var registerError = function(number, message, error) {
- errors[errors.length] = { number: number, reason: message, error: error };
- numberCompleted();
- }
+ return function(numbers, message, callback) {
+ var numbersCompleted = 0;
+ var errors = [];
+ var successfulNumbers = [];
- var doSendMessage = function(number, devicesForNumber, message) {
- return sendMessageToDevices(number, devicesForNumber, message).then(function(result) {
- successfulNumbers[successfulNumbers.length] = number;
+ var numberCompleted = function() {
+ numbersCompleted++;
+ if (numbersCompleted >= numbers.length)
+ callback({success: successfulNumbers, failure: errors});
+ }
+
+ var registerError = function(number, message, error) {
+ errors[errors.length] = { number: number, reason: message, error: error };
numberCompleted();
- }).catch(function(error) {
- if (error instanceof Error && error.name == "HTTPError" && (error.message == 410 || error.message == 409)) {
- //TODO: Re-request keys for number here
- }
- registerError(number, "Failed to create or send message", error);
- });
- }
+ }
- for (var i = 0; i < numbers.length; i++) {
- var number = numbers[i];
- var devicesForNumber = storage.devices.getDeviceObjectListFromNumber(number);
-
- if (devicesForNumber.length == 0) {
- getKeysForNumber(number).then(function(identity_key) {
- devicesForNumber = storage.devices.getDeviceObjectListFromNumber(number);
- if (devicesForNumber.length == 0)
- registerError(number, "Failed to retreive new device keys for number " + number, null);
- else
- doSendMessage(number, devicesForNumber, message);
+ var doSendMessage = function(number, devicesForNumber, message) {
+ return sendMessageToDevices(number, devicesForNumber, message).then(function(result) {
+ successfulNumbers[successfulNumbers.length] = number;
+ numberCompleted();
}).catch(function(error) {
- registerError(number, "Failed to retreive new device keys for number " + number, error);
+ if (error instanceof Error && error.name == "HTTPError" && (error.message == 410 || error.message == 409)) {
+ //TODO: Re-request keys for number here
+ }
+ registerError(number, "Failed to create or send message", error);
});
- } else
- doSendMessage(number, devicesForNumber, message);
+ }
+
+ for (var i = 0; i < numbers.length; i++) {
+ var number = numbers[i];
+ var devicesForNumber = storage.devices.getDeviceObjectListFromNumber(number);
+
+ if (devicesForNumber.length == 0) {
+ getKeysForNumber(number).then(function(identity_key) {
+ devicesForNumber = storage.devices.getDeviceObjectListFromNumber(number);
+ if (devicesForNumber.length == 0)
+ registerError(number, "Failed to retreive new device keys for number " + number, null);
+ else
+ doSendMessage(number, devicesForNumber, message);
+ }).catch(function(error) {
+ registerError(number, "Failed to retreive new device keys for number " + number, error);
+ });
+ } else
+ doSendMessage(number, devicesForNumber, message);
+ }
}
-}
+}();
function requestIdentityPrivKeyFromMasterDevice(number, identityKey) {
sendMessageToDevices([storage.devices.getDeviceObject(getNumberFromString(number)) + ".1"],
diff --git a/js/options.js b/js/options.js
index beb4b4d1b1..2f0fabeec0 100644
--- a/js/options.js
+++ b/js/options.js
@@ -56,7 +56,7 @@ $('#init-go-single-client').click(function() {
single_device = true;
- API.requestVerificationCode(number,
+ textsecure.api.requestVerificationCode(number,
function(response) { },
function(code) {
alert("Failed to send key?" + code); //TODO
@@ -76,7 +76,7 @@ $('#init-go').click(function() {
$('#verify4done').html('');
$('#verify').show();
- API.confirmCode($('#code').val(), number, password, signaling_key, registrationId, single_device,
+ textsecure.api.confirmCode($('#code').val(), number, password, signaling_key, registrationId, single_device,
function(response) {
if (single_device)
response = 1;
@@ -91,7 +91,7 @@ $('#init-go').click(function() {
$('#verify2done').html('done');
textsecure.crypto.generateKeys().then(function(keys) {
$('#verify3done').html('done');
- API.registerKeys(keys,
+ textsecure.api.registerKeys(keys,
function(response) {
$('#complete-number').html(number);
$('#verify').hide();
@@ -105,15 +105,17 @@ $('#init-go').click(function() {
}
if (!single_device) {
- getKeysForNumber(number).then(function(identityKey) {
- subscribeToPush(function(message) {
+ //TODO: Redo all this
+ /*getKeysForNumber(number).then(function(identityKey) {
+ textsecure.subscribeToPush(function(message) {
//TODO receive shared identity key
register_keys_func();
});
requestIdentityPrivKeyFromMasterDevice(number);
}).catch(function(error) {
alert(error); //TODO
- });
+ });*/
+ register_keys_func();
} else {
register_keys_func();
}
@@ -136,7 +138,7 @@ $('#init-go').click(function() {
}
});
-registerOnLoadFunction(function() {
+textsecure.registerOnLoadFunction(function() {
if (!isRegistrationDone()) {
$('#init-setup').show();
} else {
diff --git a/js/popup.js b/js/popup.js
index 341ee4e649..be2e90c64f 100644
--- a/js/popup.js
+++ b/js/popup.js
@@ -23,7 +23,7 @@ $('#send_link').click(function() {
$('#send').show();
});
-registerOnLoadFunction(function() {
+textsecure.registerOnLoadFunction(function() {
if (storage.getUnencrypted("number_id") === undefined) {
chrome.tabs.create({url: "options.html"});
} else {
@@ -73,7 +73,7 @@ registerOnLoadFunction(function() {
var messageProto = new PushMessageContentProtobuf();
messageProto.body = input.val();
- sendMessageToNumbers(sendDestinations, messageProto, function(result) {
+ textsecure.sendMessage(sendDestinations, messageProto, function(result) {
console.log(result);
button.removeAttr("disabled");
button.text("Send");
@@ -109,7 +109,7 @@ registerOnLoadFunction(function() {
}
var messageProto = new PushMessageContentProtobuf();
messageProto.body = $("#popup_send_text").val();
- sendMessageToNumbers(numbers, messageProto,
+ textsecure.sendMessage(numbers, messageProto,
//TODO: Handle result
function(thing) {console.log(thing);});
});
diff --git a/js/test.js b/js/test.js
index 2d57501c38..d06ffa30ed 100644
--- a/js/test.js
+++ b/js/test.js
@@ -95,7 +95,7 @@ function hexToArrayBuffer(str) {
return ret;
}
-registerOnLoadFunction(function() {
+textsecure.registerOnLoadFunction(function() {
localStorage.clear();
// Random tests to check my JS knowledge