From 1c76c0b5462e3024c2030ab234e3d5da0cee32a7 Mon Sep 17 00:00:00 2001 From: lilia Date: Tue, 28 Oct 2014 20:30:35 -0700 Subject: [PATCH] Move storage objects to their own files Greatly reduce the size of the ignominiously named helpers.js. --- background.html | 5 +- index.html | 3 + js/helpers.js | 262 ------------------------------------------ js/storage.js | 69 +++++++++++ js/storage/devices.js | 111 ++++++++++++++++++ js/storage/groups.js | 140 ++++++++++++++++++++++ options.html | 3 + test.html | 3 + test/test_views.html | 3 + 9 files changed, 336 insertions(+), 263 deletions(-) create mode 100644 js/storage.js create mode 100644 js/storage/devices.js create mode 100644 js/storage/groups.js diff --git a/background.html b/background.html index b0e186ba0f..6d10b280f5 100644 --- a/background.html +++ b/background.html @@ -29,7 +29,10 @@ - + + + + diff --git a/index.html b/index.html index f4b71d8039..50c7bf9477 100644 --- a/index.html +++ b/index.html @@ -138,6 +138,9 @@ + + + diff --git a/js/helpers.js b/js/helpers.js index 5375dfcc55..47e4df3e06 100644 --- a/js/helpers.js +++ b/js/helpers.js @@ -205,268 +205,6 @@ window.textsecure.throwHumanError = function(error, type, humanError) { throw e; } -/************************************************ - *** Utilities to store data in local storage *** - ************************************************/ -window.textsecure.storage = function() { - var self = {}; - - /***************************** - *** Base Storage Routines *** - *****************************/ - self.putEncrypted = function(key, value) { - //TODO - if (value === undefined) - throw new Error("Tried to store undefined"); - localStorage.setItem("e" + key, textsecure.utils.jsonThing(value)); - } - - self.getEncrypted = function(key, defaultValue) { - //TODO - var value = localStorage.getItem("e" + key); - if (value === null) - return defaultValue; - return JSON.parse(value); - } - - self.removeEncrypted = function(key) { - localStorage.removeItem("e" + key); - } - - self.putUnencrypted = function(key, value) { - if (value === undefined) - throw new Error("Tried to store undefined"); - localStorage.setItem("u" + key, textsecure.utils.jsonThing(value)); - } - - self.getUnencrypted = function(key, defaultValue) { - var value = localStorage.getItem("u" + key); - if (value === null) - return defaultValue; - return JSON.parse(value); - } - - self.removeUnencrypted = function(key) { - localStorage.removeItem("u" + key); - } - - /********************** - *** Device Storage *** - **********************/ - self.devices = function() { - var self = {}; - - var internalSaveDeviceObject = function(deviceObject, onlyKeys) { - if (deviceObject.identityKey === undefined || deviceObject.encodedNumber === undefined) - throw new Error("Tried to store invalid deviceObject"); - - var number = textsecure.utils.unencodeNumber(deviceObject.encodedNumber)[0]; - var map = textsecure.storage.getEncrypted("devices" + number); - - if (map === undefined) - map = { devices: [deviceObject], identityKey: deviceObject.identityKey }; - else if (map.identityKey != getString(deviceObject.identityKey)) - throw new Error("Identity key changed"); - else { - var updated = false; - for (var i in map.devices) { - if (map.devices[i].encodedNumber == deviceObject.encodedNumber) { - if (!onlyKeys) - map.devices[i] = deviceObject; - else { - map.devices[i].preKey = deviceObject.preKey; - map.devices[i].preKeyId = deviceObject.preKeyId; - map.devices[i].signedKey = deviceObject.signedKey; - map.devices[i].signedKeyId = deviceObject.signedKeyId; - map.devices[i].registrationId = deviceObject.registrationId; - } - updated = true; - } - } - - if (!updated) - map.devices.push(deviceObject); - } - - textsecure.storage.putEncrypted("devices" + number, map); - } - - self.saveDeviceObject = function(deviceObject) { - return internalSaveDeviceObject(deviceObject, false); - } - - self.saveKeysToDeviceObject = function(deviceObject) { - return internalSaveDeviceObject(deviceObject, true); - } - - self.getDeviceObjectsForNumber = function(number) { - var map = textsecure.storage.getEncrypted("devices" + number); - return map === undefined ? [] : map.devices; - } - - self.getDeviceObject = function(encodedNumber) { - var number = textsecure.utils.unencodeNumber(encodedNumber); - var devices = self.getDeviceObjectsForNumber(number[0]); - if (devices === undefined) - return undefined; - - for (var i in devices) - if (devices[i].encodedNumber == encodedNumber) - return devices[i]; - - return undefined; - } - - self.removeDeviceIdsForNumber = function(number, deviceIdsToRemove) { - var map = textsecure.storage.getEncrypted("devices" + number); - if (map === undefined) - throw new Error("Tried to remove device for unknown number"); - - var newDevices = []; - var devicesRemoved = 0; - for (var i in map.devices) { - var keep = true; - for (var j in deviceIdsToRemove) - if (map.devices[i].encodedNumber == number + "." + deviceIdsToRemove[j]) - keep = false; - - if (keep) - newDevices.push(map.devices[i]); - else - devicesRemoved++; - } - - if (devicesRemoved != deviceIdsToRemove.length) - throw new Error("Tried to remove unknown device"); - } - - return self; - }(); - - /********************* - *** Group Storage *** - *********************/ - self.groups = function() { - var self = {}; - - var addGroupToNumber = function(groupId, number) { - var membership = textsecure.storage.getEncrypted("groupMembership" + number, [groupId]); - if (membership.indexOf(groupId) < 0) - membership.push(groupId); - textsecure.storage.putEncrypted("groupMembership" + number, membership); - } - - var removeGroupFromNumber = function(groupId, number) { - var membership = textsecure.storage.getEncrypted("groupMembership" + number, [groupId]); - membership = membership.filter(function(group) { return group != groupId; }); - if (membership.length == 0) - textsecure.storage.removeEncrypted("groupMembership" + number); - else - textsecure.storage.putEncrypted("groupMembership" + number, membership); - } - - self.getGroupListForNumber = function(number) { - return textsecure.storage.getEncrypted("groupMembership" + number, []); - } - - self.createNewGroup = function(numbers, groupId) { - if (groupId !== undefined && textsecure.storage.getEncrypted("group" + groupId) !== undefined) { - throw new Error("Tried to recreate group"); - } - - while (groupId === undefined || textsecure.storage.getEncrypted("group" + groupId) !== undefined) { - groupId = getString(textsecure.crypto.getRandomBytes(16)); - } - - var me = textsecure.utils.unencodeNumber(textsecure.storage.getUnencrypted("number_id"))[0]; - var haveMe = false; - var finalNumbers = []; - for (var i in numbers) { - var number = libphonenumber.util.verifyNumber(numbers[i]); - if (number == me) - haveMe = true; - if (finalNumbers.indexOf(number) < 0) { - finalNumbers.push(number); - addGroupToNumber(groupId, number); - } - } - - if (!haveMe) - finalNumbers.push(me); - - textsecure.storage.putEncrypted("group" + groupId, {numbers: finalNumbers}); - - return {id: groupId, numbers: finalNumbers}; - } - - self.getNumbers = function(groupId) { - var group = textsecure.storage.getEncrypted("group" + groupId); - if (group === undefined) - return undefined; - - return group.numbers; - } - - self.removeNumber = function(groupId, number) { - var group = textsecure.storage.getEncrypted("group" + groupId); - if (group === undefined) - return undefined; - - try { - number = libphonenumber.util.verifyNumber(number); - } catch (e) { - return group.numbers; - } - - var me = textsecure.utils.unencodeNumber(textsecure.storage.getUnencrypted("number_id"))[0]; - if (number == me) - throw new Error("Cannot remove ourselves from a group, leave the group instead"); - - var i = group.numbers.indexOf(number); - if (i > -1) { - group.numbers.slice(i, 1); - textsecure.storage.putEncrypted("group" + groupId, group); - removeGroupFromNumber(groupId, number); - } - - return group.numbers; - } - - self.addNumbers = function(groupId, numbers) { - var group = textsecure.storage.getEncrypted("group" + groupId); - if (group === undefined) - return undefined; - - for (var i in numbers) { - var number = libphonenumber.util.verifyNumber(numbers[i]); - if (group.numbers.indexOf(number) < 0) { - group.numbers.push(number); - addGroupToNumber(groupId, number); - } - } - - textsecure.storage.putEncrypted("group" + groupId, group); - return group.numbers; - } - - self.deleteGroup = function(groupId) { - textsecure.storage.removeEncrypted("group" + groupId); - } - - self.getGroup = function(groupId) { - var group = textsecure.storage.getEncrypted("group" + groupId); - if (group === undefined) - return undefined; - - return { id: groupId, numbers: group.numbers }; //TODO: avatar/name tracking - } - - return self; - }(); - - return self; -}(); - /********************** *** NaCL Interface *** **********************/ diff --git a/js/storage.js b/js/storage.js new file mode 100644 index 0000000000..cae05ad199 --- /dev/null +++ b/js/storage.js @@ -0,0 +1,69 @@ +/* vim: ts=4:sw=4 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +'use strict'; + +;(function() { + + /************************************************ + *** Utilities to store data in local storage *** + ************************************************/ + window.textsecure = window.textsecure || {}; + window.textsecure.storage = window.textsecure.storage || {}; + + window.textsecure.storage = { + + /***************************** + *** Base Storage Routines *** + *****************************/ + putEncrypted: function(key, value) { + //TODO + if (value === undefined) + throw new Error("Tried to store undefined"); + localStorage.setItem("e" + key, textsecure.utils.jsonThing(value)); + }, + + getEncrypted: function(key, defaultValue) { + //TODO + var value = localStorage.getItem("e" + key); + if (value === null) + return defaultValue; + return JSON.parse(value); + }, + + removeEncrypted: function(key) { + localStorage.removeItem("e" + key); + }, + + putUnencrypted: function(key, value) { + if (value === undefined) + throw new Error("Tried to store undefined"); + localStorage.setItem("u" + key, textsecure.utils.jsonThing(value)); + }, + + getUnencrypted: function(key, defaultValue) { + var value = localStorage.getItem("u" + key); + if (value === null) + return defaultValue; + return JSON.parse(value); + }, + + removeUnencrypted: function(key) { + localStorage.removeItem("u" + key); + } + }; +})(); + diff --git a/js/storage/devices.js b/js/storage/devices.js new file mode 100644 index 0000000000..2fd86e7d01 --- /dev/null +++ b/js/storage/devices.js @@ -0,0 +1,111 @@ +/* vim: ts=4:sw=4 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +'use strict'; + +;(function() { + /********************** + *** Device Storage *** + **********************/ + window.textsecure = window.textsecure || {}; + window.textsecure.storage = window.textsecure.storage || {}; + + window.textsecure.storage.devices = { + saveDeviceObject: function(deviceObject) { + return internalSaveDeviceObject(deviceObject, false); + }, + + saveKeysToDeviceObject: function(deviceObject) { + return internalSaveDeviceObject(deviceObject, true); + }, + + getDeviceObjectsForNumber: function(number) { + var map = textsecure.storage.getEncrypted("devices" + number); + return map === undefined ? [] : map.devices; + }, + + getDeviceObject: function(encodedNumber) { + var number = textsecure.utils.unencodeNumber(encodedNumber); + var devices = textsecure.storage.devices.getDeviceObjectsForNumber(number[0]); + if (devices === undefined) + return undefined; + + for (var i in devices) + if (devices[i].encodedNumber == encodedNumber) + return devices[i]; + + return undefined; + }, + + removeDeviceIdsForNumber: function(number, deviceIdsToRemove) { + var map = textsecure.storage.getEncrypted("devices" + number); + if (map === undefined) + throw new Error("Tried to remove device for unknown number"); + + var newDevices = []; + var devicesRemoved = 0; + for (var i in map.devices) { + var keep = true; + for (var j in deviceIdsToRemove) + if (map.devices[i].encodedNumber == number + "." + deviceIdsToRemove[j]) + keep = false; + + if (keep) + newDevices.push(map.devices[i]); + else + devicesRemoved++; + } + + if (devicesRemoved != deviceIdsToRemove.length) + throw new Error("Tried to remove unknown device"); + } + }; + + var internalSaveDeviceObject = function(deviceObject, onlyKeys) { + if (deviceObject.identityKey === undefined || deviceObject.encodedNumber === undefined) + throw new Error("Tried to store invalid deviceObject"); + + var number = textsecure.utils.unencodeNumber(deviceObject.encodedNumber)[0]; + var map = textsecure.storage.getEncrypted("devices" + number); + + if (map === undefined) + map = { devices: [deviceObject], identityKey: deviceObject.identityKey }; + else if (map.identityKey != getString(deviceObject.identityKey)) + throw new Error("Identity key changed"); + else { + var updated = false; + for (var i in map.devices) { + if (map.devices[i].encodedNumber == deviceObject.encodedNumber) { + if (!onlyKeys) + map.devices[i] = deviceObject; + else { + map.devices[i].preKey = deviceObject.preKey; + map.devices[i].preKeyId = deviceObject.preKeyId; + map.devices[i].signedKey = deviceObject.signedKey; + map.devices[i].signedKeyId = deviceObject.signedKeyId; + map.devices[i].registrationId = deviceObject.registrationId; + } + updated = true; + } + } + + if (!updated) + map.devices.push(deviceObject); + } + + textsecure.storage.putEncrypted("devices" + number, map); + }; +})(); diff --git a/js/storage/groups.js b/js/storage/groups.js new file mode 100644 index 0000000000..3de9c24b75 --- /dev/null +++ b/js/storage/groups.js @@ -0,0 +1,140 @@ +/* vim: ts=4:sw=4 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +'use strict'; + +;(function() { + /********************* + *** Group Storage *** + *********************/ + window.textsecure = window.textsecure || {}; + window.textsecure.storage = window.textsecure.storage || {}; + + window.textsecure.storage.groups = { + getGroupListForNumber: function(number) { + return textsecure.storage.getEncrypted("groupMembership" + number, []); + }, + + createNewGroup: function(numbers, groupId) { + if (groupId !== undefined && textsecure.storage.getEncrypted("group" + groupId) !== undefined) { + throw new Error("Tried to recreate group"); + } + + while (groupId === undefined || textsecure.storage.getEncrypted("group" + groupId) !== undefined) { + groupId = getString(textsecure.crypto.getRandomBytes(16)); + } + + var me = textsecure.utils.unencodeNumber(textsecure.storage.getUnencrypted("number_id"))[0]; + var haveMe = false; + var finalNumbers = []; + for (var i in numbers) { + var number = libphonenumber.util.verifyNumber(numbers[i]); + if (number == me) + haveMe = true; + if (finalNumbers.indexOf(number) < 0) { + finalNumbers.push(number); + addGroupToNumber(groupId, number); + } + } + + if (!haveMe) + finalNumbers.push(me); + + textsecure.storage.putEncrypted("group" + groupId, {numbers: finalNumbers}); + + return {id: groupId, numbers: finalNumbers}; + }, + + getNumbers: function(groupId) { + var group = textsecure.storage.getEncrypted("group" + groupId); + if (group === undefined) + return undefined; + + return group.numbers; + }, + + removeNumber: function(groupId, number) { + var group = textsecure.storage.getEncrypted("group" + groupId); + if (group === undefined) + return undefined; + + try { + number = libphonenumber.util.verifyNumber(number); + } catch (e) { + return group.numbers; + } + + var me = textsecure.utils.unencodeNumber(textsecure.storage.getUnencrypted("number_id"))[0]; + if (number == me) + throw new Error("Cannot remove ourselves from a group, leave the group instead"); + + var i = group.numbers.indexOf(number); + if (i > -1) { + group.numbers.slice(i, 1); + textsecure.storage.putEncrypted("group" + groupId, group); + removeGroupFromNumber(groupId, number); + } + + return group.numbers; + }, + + addNumbers: function(groupId, numbers) { + var group = textsecure.storage.getEncrypted("group" + groupId); + if (group === undefined) + return undefined; + + for (var i in numbers) { + var number = libphonenumber.util.verifyNumber(numbers[i]); + if (group.numbers.indexOf(number) < 0) { + group.numbers.push(number); + addGroupToNumber(groupId, number); + } + } + + textsecure.storage.putEncrypted("group" + groupId, group); + return group.numbers; + }, + + deleteGroup: function(groupId) { + textsecure.storage.removeEncrypted("group" + groupId); + }, + + getGroup: function(groupId) { + var group = textsecure.storage.getEncrypted("group" + groupId); + if (group === undefined) + return undefined; + + return { id: groupId, numbers: group.numbers }; //TODO: avatar/name tracking + } + }; + + var addGroupToNumber = function(groupId, number) { + var membership = textsecure.storage.getEncrypted("groupMembership" + number, [groupId]); + if (membership.indexOf(groupId) < 0) + membership.push(groupId); + textsecure.storage.putEncrypted("groupMembership" + number, membership); + } + + var removeGroupFromNumber = function(groupId, number) { + var membership = textsecure.storage.getEncrypted("groupMembership" + number, [groupId]); + membership = membership.filter(function(group) { return group != groupId; }); + if (membership.length == 0) + textsecure.storage.removeEncrypted("groupMembership" + number); + else + textsecure.storage.putEncrypted("groupMembership" + number, membership); + } + +})(); diff --git a/options.html b/options.html index 961181285a..6d63f2fe65 100644 --- a/options.html +++ b/options.html @@ -106,6 +106,9 @@ + + + diff --git a/test.html b/test.html index bfe95511e4..84096ddc26 100644 --- a/test.html +++ b/test.html @@ -47,6 +47,9 @@ + + + diff --git a/test/test_views.html b/test/test_views.html index 6ced2533f8..29af628b38 100644 --- a/test/test_views.html +++ b/test/test_views.html @@ -127,6 +127,9 @@ + + +