Enforce stronger types for ArrayBuffers and storage
This commit is contained in:
parent
61ac79e9ae
commit
8f5086227a
56 changed files with 748 additions and 675 deletions
|
@ -332,11 +332,8 @@
|
||||||
<script type='text/javascript' src='ts/backboneJquery.js'></script>
|
<script type='text/javascript' src='ts/backboneJquery.js'></script>
|
||||||
<script type='text/javascript' src='js/reliable_trigger.js'></script>
|
<script type='text/javascript' src='js/reliable_trigger.js'></script>
|
||||||
<script type='text/javascript' src='js/database.js'></script>
|
<script type='text/javascript' src='js/database.js'></script>
|
||||||
<script type='text/javascript' src='js/storage.js'></script>
|
|
||||||
|
|
||||||
<script type='text/javascript' src='libtextsecure/protocol_wrapper.js'></script>
|
<script type='text/javascript' src='libtextsecure/protocol_wrapper.js'></script>
|
||||||
<script type='text/javascript' src='libtextsecure/storage/user.js'></script>
|
|
||||||
<script type='text/javascript' src='libtextsecure/storage/unprocessed.js'></script>
|
|
||||||
<script type='text/javascript' src='libtextsecure/protobufs.js'></script>
|
<script type='text/javascript' src='libtextsecure/protobufs.js'></script>
|
||||||
|
|
||||||
<script type='text/javascript' src='js/notifications.js'></script>
|
<script type='text/javascript' src='js/notifications.js'></script>
|
||||||
|
@ -348,7 +345,6 @@
|
||||||
<script type='text/javascript' src='js/reactions.js'></script>
|
<script type='text/javascript' src='js/reactions.js'></script>
|
||||||
<script type='text/javascript' src='js/deletes.js'></script>
|
<script type='text/javascript' src='js/deletes.js'></script>
|
||||||
<script type='text/javascript' src='js/libphonenumber-util.js'></script>
|
<script type='text/javascript' src='js/libphonenumber-util.js'></script>
|
||||||
<script type='text/javascript' src='js/models/blockedNumbers.js'></script>
|
|
||||||
<script type='text/javascript' src='js/expiring_messages.js'></script>
|
<script type='text/javascript' src='js/expiring_messages.js'></script>
|
||||||
<script type='text/javascript' src='js/expiring_tap_to_view_messages.js'></script>
|
<script type='text/javascript' src='js/expiring_tap_to_view_messages.js'></script>
|
||||||
|
|
||||||
|
|
|
@ -1,101 +0,0 @@
|
||||||
// Copyright 2016-2020 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
/* global storage, _ */
|
|
||||||
|
|
||||||
// eslint-disable-next-line func-names
|
|
||||||
(function () {
|
|
||||||
const BLOCKED_NUMBERS_ID = 'blocked';
|
|
||||||
const BLOCKED_UUIDS_ID = 'blocked-uuids';
|
|
||||||
const BLOCKED_GROUPS_ID = 'blocked-groups';
|
|
||||||
|
|
||||||
function getArray(key) {
|
|
||||||
const result = storage.get(key, []);
|
|
||||||
|
|
||||||
if (!Array.isArray(result)) {
|
|
||||||
window.log.error(
|
|
||||||
`Expected storage key ${JSON.stringify(
|
|
||||||
key
|
|
||||||
)} to contain an array or nothing`
|
|
||||||
);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
storage.getBlockedNumbers = () => getArray(BLOCKED_NUMBERS_ID);
|
|
||||||
storage.isBlocked = number => {
|
|
||||||
const numbers = storage.getBlockedNumbers();
|
|
||||||
|
|
||||||
return _.include(numbers, number);
|
|
||||||
};
|
|
||||||
storage.addBlockedNumber = number => {
|
|
||||||
const numbers = storage.getBlockedNumbers();
|
|
||||||
if (_.include(numbers, number)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.log.info('adding', number, 'to blocked list');
|
|
||||||
storage.put(BLOCKED_NUMBERS_ID, numbers.concat(number));
|
|
||||||
};
|
|
||||||
storage.removeBlockedNumber = number => {
|
|
||||||
const numbers = storage.getBlockedNumbers();
|
|
||||||
if (!_.include(numbers, number)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.log.info('removing', number, 'from blocked list');
|
|
||||||
storage.put(BLOCKED_NUMBERS_ID, _.without(numbers, number));
|
|
||||||
};
|
|
||||||
|
|
||||||
storage.getBlockedUuids = () => getArray(BLOCKED_UUIDS_ID);
|
|
||||||
storage.isUuidBlocked = uuid => {
|
|
||||||
const uuids = storage.getBlockedUuids();
|
|
||||||
|
|
||||||
return _.include(uuids, uuid);
|
|
||||||
};
|
|
||||||
storage.addBlockedUuid = uuid => {
|
|
||||||
const uuids = storage.getBlockedUuids();
|
|
||||||
if (_.include(uuids, uuid)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.log.info('adding', uuid, 'to blocked list');
|
|
||||||
storage.put(BLOCKED_UUIDS_ID, uuids.concat(uuid));
|
|
||||||
};
|
|
||||||
storage.removeBlockedUuid = uuid => {
|
|
||||||
const numbers = storage.getBlockedUuids();
|
|
||||||
if (!_.include(numbers, uuid)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.log.info('removing', uuid, 'from blocked list');
|
|
||||||
storage.put(BLOCKED_UUIDS_ID, _.without(numbers, uuid));
|
|
||||||
};
|
|
||||||
|
|
||||||
storage.getBlockedGroups = () => getArray(BLOCKED_GROUPS_ID);
|
|
||||||
storage.isGroupBlocked = groupId => {
|
|
||||||
const groupIds = storage.getBlockedGroups();
|
|
||||||
|
|
||||||
return _.include(groupIds, groupId);
|
|
||||||
};
|
|
||||||
storage.addBlockedGroup = groupId => {
|
|
||||||
const groupIds = storage.getBlockedGroups();
|
|
||||||
if (_.include(groupIds, groupId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.log.info(`adding group(${groupId}) to blocked list`);
|
|
||||||
storage.put(BLOCKED_GROUPS_ID, groupIds.concat(groupId));
|
|
||||||
};
|
|
||||||
storage.removeBlockedGroup = groupId => {
|
|
||||||
const groupIds = storage.getBlockedGroups();
|
|
||||||
if (!_.include(groupIds, groupId)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.log.info(`removing group(${groupId} from blocked list`);
|
|
||||||
storage.put(BLOCKED_GROUPS_ID, _.without(groupIds, groupId));
|
|
||||||
};
|
|
||||||
})();
|
|
131
js/storage.js
131
js/storage.js
|
@ -1,131 +0,0 @@
|
||||||
// Copyright 2014-2020 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
/* global _ */
|
|
||||||
/* eslint-disable more/no-then */
|
|
||||||
|
|
||||||
// eslint-disable-next-line func-names
|
|
||||||
(function () {
|
|
||||||
window.Whisper = window.Whisper || {};
|
|
||||||
|
|
||||||
let ready = false;
|
|
||||||
let items;
|
|
||||||
let callbacks = [];
|
|
||||||
|
|
||||||
reset();
|
|
||||||
|
|
||||||
async function put(key, value) {
|
|
||||||
if (value === undefined) {
|
|
||||||
window.log.warn(`storage/put: undefined provided for key ${key}`);
|
|
||||||
}
|
|
||||||
if (!ready) {
|
|
||||||
window.log.warn('Called storage.put before storage is ready. key:', key);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = { id: key, value };
|
|
||||||
|
|
||||||
items[key] = data;
|
|
||||||
await window.Signal.Data.createOrUpdateItem(data);
|
|
||||||
|
|
||||||
if (_.has(window, ['reduxActions', 'items', 'putItemExternal'])) {
|
|
||||||
window.reduxActions.items.putItemExternal(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function get(key, defaultValue) {
|
|
||||||
if (!ready) {
|
|
||||||
window.log.warn('Called storage.get before storage is ready. key:', key);
|
|
||||||
}
|
|
||||||
|
|
||||||
const item = items[key];
|
|
||||||
if (!item) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return item.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function remove(key) {
|
|
||||||
if (!ready) {
|
|
||||||
window.log.warn(
|
|
||||||
'Called storage.remove before storage is ready. key:',
|
|
||||||
key
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete items[key];
|
|
||||||
await window.Signal.Data.removeItemById(key);
|
|
||||||
|
|
||||||
if (_.has(window, ['reduxActions', 'items', 'removeItemExternal'])) {
|
|
||||||
window.reduxActions.items.removeItemExternal(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onready(callback) {
|
|
||||||
if (ready) {
|
|
||||||
callback();
|
|
||||||
} else {
|
|
||||||
callbacks.push(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function callListeners() {
|
|
||||||
if (ready) {
|
|
||||||
callbacks.forEach(callback => callback());
|
|
||||||
callbacks = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetch() {
|
|
||||||
this.reset();
|
|
||||||
const array = await window.Signal.Data.getAllItems();
|
|
||||||
|
|
||||||
for (let i = 0, max = array.length; i < max; i += 1) {
|
|
||||||
const item = array[i];
|
|
||||||
const { id } = item;
|
|
||||||
items[id] = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
ready = true;
|
|
||||||
callListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getItemsState() {
|
|
||||||
const data = _.clone(items);
|
|
||||||
const ids = Object.keys(data);
|
|
||||||
ids.forEach(id => {
|
|
||||||
data[id] = data[id].value;
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
ready = false;
|
|
||||||
items = Object.create(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const storage = {
|
|
||||||
fetch,
|
|
||||||
put,
|
|
||||||
get,
|
|
||||||
getItemsState,
|
|
||||||
remove,
|
|
||||||
onready,
|
|
||||||
reset,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Keep a reference to this storage system, since there are scenarios where
|
|
||||||
// we need to replace it with the legacy storage system for a while.
|
|
||||||
window.newStorage = storage;
|
|
||||||
|
|
||||||
window.textsecure = window.textsecure || {};
|
|
||||||
window.textsecure.storage = window.textsecure.storage || {};
|
|
||||||
|
|
||||||
window.installStorage = newStorage => {
|
|
||||||
window.storage = newStorage;
|
|
||||||
window.textsecure.storage.impl = newStorage;
|
|
||||||
};
|
|
||||||
|
|
||||||
window.installStorage(storage);
|
|
||||||
})();
|
|
|
@ -1,12 +1,9 @@
|
||||||
// Copyright 2016-2020 Signal Messenger, LLC
|
// Copyright 2016-2020 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
/* global window, textsecure, SignalProtocolStore */
|
/* global window, SignalProtocolStore */
|
||||||
|
|
||||||
// eslint-disable-next-line func-names
|
// eslint-disable-next-line func-names
|
||||||
(function () {
|
(function () {
|
||||||
window.textsecure = window.textsecure || {};
|
window.textsecure.storage.protocol = new SignalProtocolStore();
|
||||||
window.textsecure.storage = window.textsecure.storage || {};
|
|
||||||
|
|
||||||
textsecure.storage.protocol = new SignalProtocolStore();
|
|
||||||
})();
|
})();
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
// Copyright 2017-2020 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
/* global window, textsecure */
|
|
||||||
|
|
||||||
// eslint-disable-next-line func-names
|
|
||||||
(function () {
|
|
||||||
/** ***************************************
|
|
||||||
*** Not-yet-processed message storage ***
|
|
||||||
**************************************** */
|
|
||||||
window.textsecure = window.textsecure || {};
|
|
||||||
window.textsecure.storage = window.textsecure.storage || {};
|
|
||||||
|
|
||||||
window.textsecure.storage.unprocessed = {
|
|
||||||
getCount() {
|
|
||||||
return textsecure.storage.protocol.getUnprocessedCount();
|
|
||||||
},
|
|
||||||
getAll() {
|
|
||||||
return textsecure.storage.protocol.getAllUnprocessed();
|
|
||||||
},
|
|
||||||
get(id) {
|
|
||||||
return textsecure.storage.protocol.getUnprocessedById(id);
|
|
||||||
},
|
|
||||||
updateAttempts(id, attempts) {
|
|
||||||
return textsecure.storage.protocol.updateUnprocessedAttempts(
|
|
||||||
id,
|
|
||||||
attempts
|
|
||||||
);
|
|
||||||
},
|
|
||||||
addDecryptedData(id, data) {
|
|
||||||
return textsecure.storage.protocol.updateUnprocessedWithData(id, data);
|
|
||||||
},
|
|
||||||
addDecryptedDataToList(array) {
|
|
||||||
return textsecure.storage.protocol.updateUnprocessedsWithData(array);
|
|
||||||
},
|
|
||||||
remove(idOrArray) {
|
|
||||||
return textsecure.storage.protocol.removeUnprocessed(idOrArray);
|
|
||||||
},
|
|
||||||
removeAll() {
|
|
||||||
return textsecure.storage.protocol.removeAllUnprocessed();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})();
|
|
|
@ -1,70 +0,0 @@
|
||||||
// Copyright 2015-2020 Signal Messenger, LLC
|
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
/* global textsecure, window */
|
|
||||||
|
|
||||||
// eslint-disable-next-line func-names
|
|
||||||
(function () {
|
|
||||||
/** *******************************************
|
|
||||||
*** Utilities to store data about the user ***
|
|
||||||
********************************************* */
|
|
||||||
window.textsecure = window.textsecure || {};
|
|
||||||
window.textsecure.storage = window.textsecure.storage || {};
|
|
||||||
|
|
||||||
window.textsecure.storage.user = {
|
|
||||||
setNumberAndDeviceId(number, deviceId, deviceName) {
|
|
||||||
textsecure.storage.put('number_id', `${number}.${deviceId}`);
|
|
||||||
if (deviceName) {
|
|
||||||
textsecure.storage.put('device_name', deviceName);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setUuidAndDeviceId(uuid, deviceId) {
|
|
||||||
textsecure.storage.put('uuid_id', `${uuid}.${deviceId}`);
|
|
||||||
},
|
|
||||||
|
|
||||||
getNumber() {
|
|
||||||
const numberId = textsecure.storage.get('number_id');
|
|
||||||
if (numberId === undefined) return undefined;
|
|
||||||
return textsecure.utils.unencodeNumber(numberId)[0];
|
|
||||||
},
|
|
||||||
|
|
||||||
getUuid() {
|
|
||||||
const uuid = textsecure.storage.get('uuid_id');
|
|
||||||
if (uuid === undefined) return undefined;
|
|
||||||
return textsecure.utils.unencodeNumber(uuid.toLowerCase())[0];
|
|
||||||
},
|
|
||||||
|
|
||||||
getDeviceId() {
|
|
||||||
return this._getDeviceIdFromUuid() || this._getDeviceIdFromNumber();
|
|
||||||
},
|
|
||||||
|
|
||||||
_getDeviceIdFromUuid() {
|
|
||||||
const uuid = textsecure.storage.get('uuid_id');
|
|
||||||
if (uuid === undefined) return undefined;
|
|
||||||
return textsecure.utils.unencodeNumber(uuid)[1];
|
|
||||||
},
|
|
||||||
|
|
||||||
_getDeviceIdFromNumber() {
|
|
||||||
const numberId = textsecure.storage.get('number_id');
|
|
||||||
if (numberId === undefined) return undefined;
|
|
||||||
return textsecure.utils.unencodeNumber(numberId)[1];
|
|
||||||
},
|
|
||||||
|
|
||||||
getDeviceName() {
|
|
||||||
return textsecure.storage.get('device_name');
|
|
||||||
},
|
|
||||||
|
|
||||||
setDeviceNameEncrypted() {
|
|
||||||
return textsecure.storage.put('deviceNameEncrypted', true);
|
|
||||||
},
|
|
||||||
|
|
||||||
getDeviceNameEncrypted() {
|
|
||||||
return textsecure.storage.get('deviceNameEncrypted');
|
|
||||||
},
|
|
||||||
|
|
||||||
getSignalingKey() {
|
|
||||||
return textsecure.storage.get('signaling_key');
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})();
|
|
|
@ -22,18 +22,12 @@
|
||||||
|
|
||||||
<script type="text/javascript" src="../components.js"></script>
|
<script type="text/javascript" src="../components.js"></script>
|
||||||
<script type="text/javascript" src="../protobufs.js" data-cover></script>
|
<script type="text/javascript" src="../protobufs.js" data-cover></script>
|
||||||
<script type="text/javascript" src="../storage/user.js" data-cover></script>
|
|
||||||
<script type="text/javascript" src="../storage/unprocessed.js" data-cover></script>
|
|
||||||
<script type="text/javascript" src="../protocol_wrapper.js" data-cover></script>
|
<script type="text/javascript" src="../protocol_wrapper.js" data-cover></script>
|
||||||
|
|
||||||
<script type="text/javascript" src="../../js/libphonenumber-util.js"></script>
|
<script type="text/javascript" src="../../js/libphonenumber-util.js"></script>
|
||||||
<script type="text/javascript" src="../../js/components.js" data-cover></script>
|
<script type="text/javascript" src="../../js/components.js" data-cover></script>
|
||||||
<script type="text/javascript" src="../../js/signal_protocol_store.js" data-cover></script>
|
|
||||||
<script type="text/javascript" src="../../js/storage.js" data-cover></script>
|
|
||||||
<script type="text/javascript" src="../../js/models/blockedNumbers.js" data-cover></script>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="helpers_test.js"></script>
|
<script type="text/javascript" src="helpers_test.js"></script>
|
||||||
<script type="text/javascript" src="storage_test.js"></script>
|
|
||||||
<script type="text/javascript" src="crypto_test.js"></script>
|
<script type="text/javascript" src="crypto_test.js"></script>
|
||||||
<script type="text/javascript" src="contacts_parser_test.js"></script>
|
<script type="text/javascript" src="contacts_parser_test.js"></script>
|
||||||
<script type="text/javascript" src="generate_keys_test.js"></script>
|
<script type="text/javascript" src="generate_keys_test.js"></script>
|
||||||
|
|
|
@ -10,11 +10,8 @@
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="text/javascript" src="../../js/components.js"></script>
|
<script type="text/javascript" src="../../js/components.js"></script>
|
||||||
<script type="text/javascript" src="../../ts/backbonejQuery.js"></script>
|
<script type="text/javascript" src="../../ts/backbonejQuery.js"></script>
|
||||||
<script type="text/javascript" src="../../js/storage.js"></script>
|
|
||||||
|
|
||||||
<script type='text/javascript' src='../../libtextsecure/protocol_wrapper.js'></script>
|
<script type='text/javascript' src='../../libtextsecure/protocol_wrapper.js'></script>
|
||||||
<script type='text/javascript' src='../../libtextsecure/storage/user.js'></script>
|
|
||||||
<script type='text/javascript' src='../../libtextsecure/storage/unprocessed.js'></script>
|
|
||||||
<script type='text/javascript' src='../../libtextsecure/protobufs.js'></script>
|
<script type='text/javascript' src='../../libtextsecure/protobufs.js'></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -342,16 +342,11 @@
|
||||||
<script type="text/javascript" src="test.js"></script>
|
<script type="text/javascript" src="test.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript" src="../js/database.js" data-cover></script>
|
<script type="text/javascript" src="../js/database.js" data-cover></script>
|
||||||
<script type="text/javascript" src="../js/storage.js" data-cover></script>
|
|
||||||
|
|
||||||
<script type="text/javascript" src="../libtextsecure/protocol_wrapper.js"></script>
|
<script type="text/javascript" src="../libtextsecure/protocol_wrapper.js"></script>
|
||||||
<script type="text/javascript" src="../libtextsecure/storage/user.js"></script>
|
|
||||||
<script type="text/javascript" src="../libtextsecure/storage/unprocessed.js"></script>
|
|
||||||
<script type="text/javascript" src="../libtextsecure/protobufs.js"></script>
|
<script type="text/javascript" src="../libtextsecure/protobufs.js"></script>
|
||||||
|
|
||||||
|
|
||||||
<script type="text/javascript" src="../js/libphonenumber-util.js"></script>
|
<script type="text/javascript" src="../js/libphonenumber-util.js"></script>
|
||||||
<script type="text/javascript" src="../js/models/blockedNumbers.js" data-cover></script>
|
|
||||||
<script type="text/javascript" src="../js/message_controller.js" data-cover></script>
|
<script type="text/javascript" src="../js/message_controller.js" data-cover></script>
|
||||||
<script type="text/javascript" src="../js/keychange_listener.js" data-cover></script>
|
<script type="text/javascript" src="../js/keychange_listener.js" data-cover></script>
|
||||||
<script type='text/javascript' src='../js/expiring_messages.js' data-cover></script>
|
<script type='text/javascript' src='../js/expiring_messages.js' data-cover></script>
|
||||||
|
@ -375,7 +370,6 @@
|
||||||
<script type="text/javascript" src="models/conversations_test.js"></script>
|
<script type="text/javascript" src="models/conversations_test.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript" src="libphonenumber_util_test.js"></script>
|
<script type="text/javascript" src="libphonenumber_util_test.js"></script>
|
||||||
<script type="text/javascript" src="storage_test.js"></script>
|
|
||||||
<script type="text/javascript" src="keychange_listener_test.js"></script>
|
<script type="text/javascript" src="keychange_listener_test.js"></script>
|
||||||
<script type="text/javascript" src="reliable_trigger_test.js"></script>
|
<script type="text/javascript" src="reliable_trigger_test.js"></script>
|
||||||
<script type="text/javascript" src="backup_test.js"></script>
|
<script type="text/javascript" src="backup_test.js"></script>
|
||||||
|
|
|
@ -26,7 +26,7 @@ type ConfigValueType = {
|
||||||
enabledAt?: number;
|
enabledAt?: number;
|
||||||
value?: unknown;
|
value?: unknown;
|
||||||
};
|
};
|
||||||
type ConfigMapType = { [key: string]: ConfigValueType };
|
export type ConfigMapType = { [key: string]: ConfigValueType };
|
||||||
type ConfigListenerType = (value: ConfigValueType) => unknown;
|
type ConfigListenerType = (value: ConfigValueType) => unknown;
|
||||||
type ConfigListenersMapType = {
|
type ConfigListenersMapType = {
|
||||||
[key: string]: Array<ConfigListenerType>;
|
[key: string]: Array<ConfigListenerType>;
|
||||||
|
|
|
@ -36,6 +36,7 @@ import {
|
||||||
KeyPairType,
|
KeyPairType,
|
||||||
IdentityKeyType,
|
IdentityKeyType,
|
||||||
SenderKeyType,
|
SenderKeyType,
|
||||||
|
SessionResetsType,
|
||||||
SessionType,
|
SessionType,
|
||||||
SignedPreKeyType,
|
SignedPreKeyType,
|
||||||
OuterSignedPrekeyType,
|
OuterSignedPrekeyType,
|
||||||
|
@ -114,8 +115,6 @@ type MapFields =
|
||||||
| 'sessions'
|
| 'sessions'
|
||||||
| 'signedPreKeys';
|
| 'signedPreKeys';
|
||||||
|
|
||||||
type SessionResetsType = Record<string, number>;
|
|
||||||
|
|
||||||
export type SessionTransactionOptions = {
|
export type SessionTransactionOptions = {
|
||||||
readonly zone?: Zone;
|
readonly zone?: Zone;
|
||||||
};
|
};
|
||||||
|
@ -1199,8 +1198,8 @@ export class SignalProtocolStore extends EventsMixin {
|
||||||
|
|
||||||
const sessionResets = window.storage.get(
|
const sessionResets = window.storage.get(
|
||||||
'sessionResets',
|
'sessionResets',
|
||||||
{}
|
<SessionResetsType>{}
|
||||||
) as SessionResetsType;
|
);
|
||||||
|
|
||||||
const lastReset = sessionResets[id];
|
const lastReset = sessionResets[id];
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
} from '@signalapp/signal-client';
|
} from '@signalapp/signal-client';
|
||||||
|
|
||||||
import { DataMessageClass } from './textsecure.d';
|
import { DataMessageClass } from './textsecure.d';
|
||||||
|
import { SessionResetsType } from './textsecure/Types.d';
|
||||||
import { MessageAttributesType } from './model-types.d';
|
import { MessageAttributesType } from './model-types.d';
|
||||||
import { WhatIsThis } from './window.d';
|
import { WhatIsThis } from './window.d';
|
||||||
import { getTitleBarVisibility, TitleBarVisibility } from './types/Settings';
|
import { getTitleBarVisibility, TitleBarVisibility } from './types/Settings';
|
||||||
|
@ -49,11 +50,10 @@ export function isOverHourIntoPast(timestamp: number): boolean {
|
||||||
return isNumber(timestamp) && isOlderThan(timestamp, HOUR);
|
return isNumber(timestamp) && isOlderThan(timestamp, HOUR);
|
||||||
}
|
}
|
||||||
|
|
||||||
type SessionResetsType = Record<string, number>;
|
|
||||||
export async function cleanupSessionResets(): Promise<void> {
|
export async function cleanupSessionResets(): Promise<void> {
|
||||||
const sessionResets = window.storage.get<SessionResetsType>(
|
const sessionResets = window.storage.get(
|
||||||
'sessionResets',
|
'sessionResets',
|
||||||
{}
|
<SessionResetsType>{}
|
||||||
);
|
);
|
||||||
|
|
||||||
const keys = Object.keys(sessionResets);
|
const keys = Object.keys(sessionResets);
|
||||||
|
@ -326,9 +326,9 @@ export async function startApp(): Promise<void> {
|
||||||
let accountManager: typeof window.textsecure.AccountManager;
|
let accountManager: typeof window.textsecure.AccountManager;
|
||||||
window.getAccountManager = () => {
|
window.getAccountManager = () => {
|
||||||
if (!accountManager) {
|
if (!accountManager) {
|
||||||
const OLD_USERNAME = window.storage.get('number_id');
|
const OLD_USERNAME = window.storage.get('number_id', '');
|
||||||
const USERNAME = window.storage.get('uuid_id');
|
const USERNAME = window.storage.get('uuid_id', '');
|
||||||
const PASSWORD = window.storage.get('password');
|
const PASSWORD = window.storage.get('password', '');
|
||||||
accountManager = new window.textsecure.AccountManager(
|
accountManager = new window.textsecure.AccountManager(
|
||||||
USERNAME || OLD_USERNAME,
|
USERNAME || OLD_USERNAME,
|
||||||
PASSWORD
|
PASSWORD
|
||||||
|
@ -498,8 +498,7 @@ export async function startApp(): Promise<void> {
|
||||||
getAutoLaunch: () => window.getAutoLaunch(),
|
getAutoLaunch: () => window.getAutoLaunch(),
|
||||||
setAutoLaunch: (value: boolean) => window.setAutoLaunch(value),
|
setAutoLaunch: (value: boolean) => window.setAutoLaunch(value),
|
||||||
|
|
||||||
// eslint-disable-next-line eqeqeq
|
isPrimary: () => window.textsecure.storage.user.getDeviceId() === 1,
|
||||||
isPrimary: () => window.textsecure.storage.user.getDeviceId() == '1',
|
|
||||||
getSyncRequest: () =>
|
getSyncRequest: () =>
|
||||||
new Promise<void>((resolve, reject) => {
|
new Promise<void>((resolve, reject) => {
|
||||||
const FIVE_MINUTES = 5 * 60 * 60 * 1000;
|
const FIVE_MINUTES = 5 * 60 * 60 * 1000;
|
||||||
|
@ -680,7 +679,7 @@ export async function startApp(): Promise<void> {
|
||||||
};
|
};
|
||||||
|
|
||||||
// How long since we were last running?
|
// How long since we were last running?
|
||||||
const lastHeartbeat = window.storage.get('lastHeartbeat');
|
const lastHeartbeat = window.storage.get('lastHeartbeat', 0);
|
||||||
await window.storage.put('lastStartup', Date.now());
|
await window.storage.put('lastStartup', Date.now());
|
||||||
|
|
||||||
const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
|
const THIRTY_DAYS = 30 * 24 * 60 * 60 * 1000;
|
||||||
|
@ -1962,10 +1961,13 @@ export async function startApp(): Promise<void> {
|
||||||
messageReceiver = null;
|
messageReceiver = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OLD_USERNAME = window.storage.get('number_id');
|
const OLD_USERNAME = window.storage.get('number_id', '');
|
||||||
const USERNAME = window.storage.get('uuid_id');
|
const USERNAME = window.storage.get('uuid_id', '');
|
||||||
const PASSWORD = window.storage.get('password');
|
const PASSWORD = window.storage.get('password', '');
|
||||||
const mySignalingKey = window.storage.get('signaling_key');
|
const mySignalingKey = window.storage.get(
|
||||||
|
'signaling_key',
|
||||||
|
new ArrayBuffer(0)
|
||||||
|
);
|
||||||
|
|
||||||
window.textsecure.messaging = new window.textsecure.MessageSender(
|
window.textsecure.messaging = new window.textsecure.MessageSender(
|
||||||
USERNAME || OLD_USERNAME,
|
USERNAME || OLD_USERNAME,
|
||||||
|
@ -2097,8 +2099,7 @@ export async function startApp(): Promise<void> {
|
||||||
!firstRun &&
|
!firstRun &&
|
||||||
connectCount === 1 &&
|
connectCount === 1 &&
|
||||||
newVersion &&
|
newVersion &&
|
||||||
// eslint-disable-next-line eqeqeq
|
window.textsecure.storage.user.getDeviceId() !== 1
|
||||||
window.textsecure.storage.user.getDeviceId() != '1'
|
|
||||||
) {
|
) {
|
||||||
window.log.info('Boot after upgrading. Requesting contact sync');
|
window.log.info('Boot after upgrading. Requesting contact sync');
|
||||||
window.getSyncRequest();
|
window.getSyncRequest();
|
||||||
|
@ -2147,11 +2148,11 @@ export async function startApp(): Promise<void> {
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
const { uuid } = await server.whoami();
|
const { uuid } = await server.whoami();
|
||||||
window.textsecure.storage.user.setUuidAndDeviceId(
|
assert(deviceId, 'We should have device id');
|
||||||
uuid,
|
window.textsecure.storage.user.setUuidAndDeviceId(uuid, deviceId);
|
||||||
deviceId as WhatIsThis
|
|
||||||
);
|
|
||||||
const ourNumber = window.textsecure.storage.user.getNumber();
|
const ourNumber = window.textsecure.storage.user.getNumber();
|
||||||
|
|
||||||
|
assert(ourNumber, 'We should have number');
|
||||||
const me = await window.ConversationController.getOrCreateAndWait(
|
const me = await window.ConversationController.getOrCreateAndWait(
|
||||||
ourNumber,
|
ourNumber,
|
||||||
'private'
|
'private'
|
||||||
|
@ -2188,7 +2189,7 @@ export async function startApp(): Promise<void> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstRun === true && deviceId !== '1') {
|
if (firstRun === true && deviceId !== 1) {
|
||||||
const hasThemeSetting = Boolean(window.storage.get('theme-setting'));
|
const hasThemeSetting = Boolean(window.storage.get('theme-setting'));
|
||||||
if (
|
if (
|
||||||
!hasThemeSetting &&
|
!hasThemeSetting &&
|
||||||
|
@ -3339,7 +3340,9 @@ export async function startApp(): Promise<void> {
|
||||||
// These two bits of data are important to ensure that the app loads up
|
// These two bits of data are important to ensure that the app loads up
|
||||||
// the conversation list, instead of showing just the QR code screen.
|
// the conversation list, instead of showing just the QR code screen.
|
||||||
window.Signal.Util.Registration.markEverDone();
|
window.Signal.Util.Registration.markEverDone();
|
||||||
|
if (previousNumberId !== undefined) {
|
||||||
await window.textsecure.storage.put(NUMBER_ID_KEY, previousNumberId);
|
await window.textsecure.storage.put(NUMBER_ID_KEY, previousNumberId);
|
||||||
|
}
|
||||||
|
|
||||||
// These two are important to ensure we don't rip through every message
|
// These two are important to ensure we don't rip through every message
|
||||||
// in the database attempting to upgrade it after starting up again.
|
// in the database attempting to upgrade it after starting up again.
|
||||||
|
@ -3347,10 +3350,14 @@ export async function startApp(): Promise<void> {
|
||||||
IS_MIGRATION_COMPLETE_KEY,
|
IS_MIGRATION_COMPLETE_KEY,
|
||||||
isMigrationComplete || false
|
isMigrationComplete || false
|
||||||
);
|
);
|
||||||
|
if (lastProcessedIndex !== undefined) {
|
||||||
await window.textsecure.storage.put(
|
await window.textsecure.storage.put(
|
||||||
LAST_PROCESSED_INDEX_KEY,
|
LAST_PROCESSED_INDEX_KEY,
|
||||||
lastProcessedIndex || null
|
lastProcessedIndex
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
await window.textsecure.storage.remove(LAST_PROCESSED_INDEX_KEY);
|
||||||
|
}
|
||||||
await window.textsecure.storage.put(VERSION_KEY, window.getVersion());
|
await window.textsecure.storage.put(VERSION_KEY, window.getVersion());
|
||||||
|
|
||||||
window.log.info('Successfully cleared local configuration');
|
window.log.info('Successfully cleared local configuration');
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { isNotNil } from './util/isNotNil';
|
||||||
import { isOlderThan } from './util/timestamp';
|
import { isOlderThan } from './util/timestamp';
|
||||||
import { parseRetryAfter } from './util/parseRetryAfter';
|
import { parseRetryAfter } from './util/parseRetryAfter';
|
||||||
import { getEnvironment, Environment } from './environment';
|
import { getEnvironment, Environment } from './environment';
|
||||||
|
import { StorageInterface } from './types/Storage.d';
|
||||||
|
|
||||||
export type ChallengeResponse = {
|
export type ChallengeResponse = {
|
||||||
readonly captcha: string;
|
readonly captcha: string;
|
||||||
|
@ -62,10 +63,7 @@ export type MinimalMessage = Pick<
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Options = {
|
export type Options = {
|
||||||
readonly storage: {
|
readonly storage: Pick<StorageInterface, 'get' | 'put'>;
|
||||||
get(key: string): ReadonlyArray<StoredEntity>;
|
|
||||||
put(key: string, value: ReadonlyArray<StoredEntity>): Promise<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
requestChallenge(request: IPCRequest): void;
|
requestChallenge(request: IPCRequest): void;
|
||||||
|
|
||||||
|
|
|
@ -2171,8 +2171,8 @@ export async function initiateMigrationToGroupV2(
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (window.storage.isGroupBlocked(previousGroupV1Id)) {
|
if (window.storage.blocked.isGroupBlocked(previousGroupV1Id)) {
|
||||||
window.storage.addBlockedGroup(groupId);
|
window.storage.blocked.addBlockedGroup(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save these most recent updates to conversation
|
// Save these most recent updates to conversation
|
||||||
|
@ -2646,8 +2646,8 @@ export async function respondToGroupV2Migration({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (window.storage.isGroupBlocked(previousGroupV1Id)) {
|
if (window.storage.blocked.isGroupBlocked(previousGroupV1Id)) {
|
||||||
window.storage.addBlockedGroup(groupId);
|
window.storage.blocked.addBlockedGroup(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save these most recent updates to conversation
|
// Save these most recent updates to conversation
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { JobQueue } from './JobQueue';
|
||||||
import { jobQueueDatabaseStore } from './JobQueueDatabaseStore';
|
import { jobQueueDatabaseStore } from './JobQueueDatabaseStore';
|
||||||
|
|
||||||
const removeStorageKeyJobDataSchema = z.object({
|
const removeStorageKeyJobDataSchema = z.object({
|
||||||
key: z.string().min(1),
|
key: z.enum(['senderCertificateWithUuid']),
|
||||||
});
|
});
|
||||||
|
|
||||||
type RemoveStorageKeyJobData = z.infer<typeof removeStorageKeyJobDataSchema>;
|
type RemoveStorageKeyJobData = z.infer<typeof removeStorageKeyJobDataSchema>;
|
||||||
|
|
|
@ -816,17 +816,17 @@ export class ConversationModel extends window.Backbone
|
||||||
isBlocked(): boolean {
|
isBlocked(): boolean {
|
||||||
const uuid = this.get('uuid');
|
const uuid = this.get('uuid');
|
||||||
if (uuid) {
|
if (uuid) {
|
||||||
return window.storage.isUuidBlocked(uuid);
|
return window.storage.blocked.isUuidBlocked(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
const e164 = this.get('e164');
|
const e164 = this.get('e164');
|
||||||
if (e164) {
|
if (e164) {
|
||||||
return window.storage.isBlocked(e164);
|
return window.storage.blocked.isBlocked(e164);
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupId = this.get('groupId');
|
const groupId = this.get('groupId');
|
||||||
if (groupId) {
|
if (groupId) {
|
||||||
return window.storage.isGroupBlocked(groupId);
|
return window.storage.blocked.isGroupBlocked(groupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -838,19 +838,19 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
const uuid = this.get('uuid');
|
const uuid = this.get('uuid');
|
||||||
if (uuid) {
|
if (uuid) {
|
||||||
window.storage.addBlockedUuid(uuid);
|
window.storage.blocked.addBlockedUuid(uuid);
|
||||||
blocked = true;
|
blocked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const e164 = this.get('e164');
|
const e164 = this.get('e164');
|
||||||
if (e164) {
|
if (e164) {
|
||||||
window.storage.addBlockedNumber(e164);
|
window.storage.blocked.addBlockedNumber(e164);
|
||||||
blocked = true;
|
blocked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupId = this.get('groupId');
|
const groupId = this.get('groupId');
|
||||||
if (groupId) {
|
if (groupId) {
|
||||||
window.storage.addBlockedGroup(groupId);
|
window.storage.blocked.addBlockedGroup(groupId);
|
||||||
blocked = true;
|
blocked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -865,19 +865,19 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
const uuid = this.get('uuid');
|
const uuid = this.get('uuid');
|
||||||
if (uuid) {
|
if (uuid) {
|
||||||
window.storage.removeBlockedUuid(uuid);
|
window.storage.blocked.removeBlockedUuid(uuid);
|
||||||
unblocked = true;
|
unblocked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const e164 = this.get('e164');
|
const e164 = this.get('e164');
|
||||||
if (e164) {
|
if (e164) {
|
||||||
window.storage.removeBlockedNumber(e164);
|
window.storage.blocked.removeBlockedNumber(e164);
|
||||||
unblocked = true;
|
unblocked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupId = this.get('groupId');
|
const groupId = this.get('groupId');
|
||||||
if (groupId) {
|
if (groupId) {
|
||||||
window.storage.removeBlockedGroup(groupId);
|
window.storage.blocked.removeBlockedGroup(groupId);
|
||||||
unblocked = true;
|
unblocked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2913,6 +2913,9 @@ export class ConversationModel extends window.Backbone
|
||||||
validateNumber(): string | null {
|
validateNumber(): string | null {
|
||||||
if (isDirectConversation(this.attributes) && this.get('e164')) {
|
if (isDirectConversation(this.attributes) && this.get('e164')) {
|
||||||
const regionCode = window.storage.get('regionCode');
|
const regionCode = window.storage.get('regionCode');
|
||||||
|
if (!regionCode) {
|
||||||
|
throw new Error('No region code');
|
||||||
|
}
|
||||||
const number = window.libphonenumber.util.parseNumber(
|
const number = window.libphonenumber.util.parseNumber(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
this.get('e164')!,
|
this.get('e164')!,
|
||||||
|
@ -5256,7 +5259,7 @@ export class ConversationModel extends window.Backbone
|
||||||
|
|
||||||
window.log.info('pinning', this.idForLogging());
|
window.log.info('pinning', this.idForLogging());
|
||||||
const pinnedConversationIds = new Set(
|
const pinnedConversationIds = new Set(
|
||||||
window.storage.get<Array<string>>('pinnedConversationIds', [])
|
window.storage.get('pinnedConversationIds', new Array<string>())
|
||||||
);
|
);
|
||||||
|
|
||||||
pinnedConversationIds.add(this.id);
|
pinnedConversationIds.add(this.id);
|
||||||
|
@ -5279,7 +5282,7 @@ export class ConversationModel extends window.Backbone
|
||||||
window.log.info('un-pinning', this.idForLogging());
|
window.log.info('un-pinning', this.idForLogging());
|
||||||
|
|
||||||
const pinnedConversationIds = new Set(
|
const pinnedConversationIds = new Set(
|
||||||
window.storage.get<Array<string>>('pinnedConversationIds', [])
|
window.storage.get('pinnedConversationIds', new Array<string>())
|
||||||
);
|
);
|
||||||
|
|
||||||
pinnedConversationIds.delete(this.id);
|
pinnedConversationIds.delete(this.id);
|
||||||
|
|
|
@ -507,7 +507,7 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
_.find(errorsForContact, error => error.name === OUTGOING_KEY_ERROR)
|
_.find(errorsForContact, error => error.name === OUTGOING_KEY_ERROR)
|
||||||
);
|
);
|
||||||
const isUnidentifiedDelivery =
|
const isUnidentifiedDelivery =
|
||||||
window.storage.get('unidentifiedDeliveryIndicators') &&
|
window.storage.get('unidentifiedDeliveryIndicators', false) &&
|
||||||
this.isUnidentifiedDelivery(id, unidentifiedLookup);
|
this.isUnidentifiedDelivery(id, unidentifiedLookup);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1189,6 +1189,9 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const regionCode = window.storage.get('regionCode');
|
const regionCode = window.storage.get('regionCode');
|
||||||
|
if (!regionCode) {
|
||||||
|
throw new Error('No region code');
|
||||||
|
}
|
||||||
const { contactSelector } = Contact;
|
const { contactSelector } = Contact;
|
||||||
const contact = contacts[0];
|
const contact = contacts[0];
|
||||||
const firstNumber =
|
const firstNumber =
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { isNormalNumber } from './util/isNormalNumber';
|
||||||
import { take } from './util/iterables';
|
import { take } from './util/iterables';
|
||||||
import { isOlderThan } from './util/timestamp';
|
import { isOlderThan } from './util/timestamp';
|
||||||
import { ConversationModel } from './models/conversations';
|
import { ConversationModel } from './models/conversations';
|
||||||
|
import { StorageInterface } from './types/Storage.d';
|
||||||
|
|
||||||
const STORAGE_KEY = 'lastAttemptedToRefreshProfilesAt';
|
const STORAGE_KEY = 'lastAttemptedToRefreshProfilesAt';
|
||||||
const MAX_AGE_TO_BE_CONSIDERED_ACTIVE = 30 * 24 * 60 * 60 * 1000;
|
const MAX_AGE_TO_BE_CONSIDERED_ACTIVE = 30 * 24 * 60 * 60 * 1000;
|
||||||
|
@ -21,13 +22,6 @@ const MAX_AGE_TO_BE_CONSIDERED_RECENTLY_REFRESHED = 1 * 24 * 60 * 60 * 1000;
|
||||||
const MAX_CONVERSATIONS_TO_REFRESH = 50;
|
const MAX_CONVERSATIONS_TO_REFRESH = 50;
|
||||||
const MIN_ELAPSED_DURATION_TO_REFRESH_AGAIN = 12 * 3600 * 1000;
|
const MIN_ELAPSED_DURATION_TO_REFRESH_AGAIN = 12 * 3600 * 1000;
|
||||||
|
|
||||||
// This type is a little stricter than what's on `window.storage`, and only requires what
|
|
||||||
// we need for easier testing.
|
|
||||||
type StorageType = {
|
|
||||||
get: (key: string) => unknown;
|
|
||||||
put: (key: string, value: unknown) => Promise<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function routineProfileRefresh({
|
export async function routineProfileRefresh({
|
||||||
allConversations,
|
allConversations,
|
||||||
ourConversationId,
|
ourConversationId,
|
||||||
|
@ -35,7 +29,7 @@ export async function routineProfileRefresh({
|
||||||
}: {
|
}: {
|
||||||
allConversations: Array<ConversationModel>;
|
allConversations: Array<ConversationModel>;
|
||||||
ourConversationId: string;
|
ourConversationId: string;
|
||||||
storage: StorageType;
|
storage: Pick<StorageInterface, 'get' | 'put'>;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
log.info('routineProfileRefresh: starting');
|
log.info('routineProfileRefresh: starting');
|
||||||
|
|
||||||
|
@ -93,7 +87,9 @@ export async function routineProfileRefresh({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasEnoughTimeElapsedSinceLastRefresh(storage: StorageType): boolean {
|
function hasEnoughTimeElapsedSinceLastRefresh(
|
||||||
|
storage: Pick<StorageInterface, 'get'>
|
||||||
|
): boolean {
|
||||||
const storedValue = storage.get(STORAGE_KEY);
|
const storedValue = storage.get(STORAGE_KEY);
|
||||||
|
|
||||||
if (isNil(storedValue)) {
|
if (isNil(storedValue)) {
|
||||||
|
|
|
@ -55,6 +55,7 @@ import {
|
||||||
uuidToArrayBuffer,
|
uuidToArrayBuffer,
|
||||||
arrayBufferToUuid,
|
arrayBufferToUuid,
|
||||||
} from '../Crypto';
|
} from '../Crypto';
|
||||||
|
import { assert } from '../util/assert';
|
||||||
import { getOwn } from '../util/getOwn';
|
import { getOwn } from '../util/getOwn';
|
||||||
import {
|
import {
|
||||||
fetchMembershipProof,
|
fetchMembershipProof,
|
||||||
|
@ -1257,9 +1258,13 @@ export class CallingClass {
|
||||||
}
|
}
|
||||||
const senderIdentityKey = senderIdentityRecord.publicKey.slice(1); // Ignore the type header, it is not used.
|
const senderIdentityKey = senderIdentityRecord.publicKey.slice(1); // Ignore the type header, it is not used.
|
||||||
|
|
||||||
const receiverIdentityRecord = window.textsecure.storage.protocol.getIdentityRecord(
|
const ourIdentifier =
|
||||||
window.textsecure.storage.user.getUuid() ||
|
window.textsecure.storage.user.getUuid() ||
|
||||||
window.textsecure.storage.user.getNumber()
|
window.textsecure.storage.user.getNumber();
|
||||||
|
assert(ourIdentifier, 'We should have either uuid or number');
|
||||||
|
|
||||||
|
const receiverIdentityRecord = window.textsecure.storage.protocol.getIdentityRecord(
|
||||||
|
ourIdentifier
|
||||||
);
|
);
|
||||||
if (!receiverIdentityRecord) {
|
if (!receiverIdentityRecord) {
|
||||||
window.log.error(
|
window.log.error(
|
||||||
|
|
|
@ -4,22 +4,16 @@
|
||||||
import { assert } from '../util/assert';
|
import { assert } from '../util/assert';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
|
|
||||||
// We define a stricter storage here that returns `unknown` instead of `any`.
|
import { StorageInterface } from '../types/Storage.d';
|
||||||
type Storage = {
|
|
||||||
get(key: string): unknown;
|
|
||||||
put(key: string, value: unknown): Promise<void>;
|
|
||||||
remove(key: string): Promise<void>;
|
|
||||||
onready: (callback: () => unknown) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class OurProfileKeyService {
|
export class OurProfileKeyService {
|
||||||
private getPromise: undefined | Promise<undefined | ArrayBuffer>;
|
private getPromise: undefined | Promise<undefined | ArrayBuffer>;
|
||||||
|
|
||||||
private promisesBlockingGet: Array<Promise<unknown>> = [];
|
private promisesBlockingGet: Array<Promise<unknown>> = [];
|
||||||
|
|
||||||
private storage?: Storage;
|
private storage?: StorageInterface;
|
||||||
|
|
||||||
initialize(storage: Storage): void {
|
initialize(storage: StorageInterface): void {
|
||||||
log.info('Our profile key service: initializing');
|
log.info('Our profile key service: initializing');
|
||||||
|
|
||||||
const storageReadyPromise = new Promise<void>(resolve => {
|
const storageReadyPromise = new Promise<void>(resolve => {
|
||||||
|
|
|
@ -13,13 +13,7 @@ import { missingCaseError } from '../util/missingCaseError';
|
||||||
import { waitForOnline } from '../util/waitForOnline';
|
import { waitForOnline } from '../util/waitForOnline';
|
||||||
import * as log from '../logging/log';
|
import * as log from '../logging/log';
|
||||||
import { connectToServerWithStoredCredentials } from '../util/connectToServerWithStoredCredentials';
|
import { connectToServerWithStoredCredentials } from '../util/connectToServerWithStoredCredentials';
|
||||||
|
import { StorageInterface } from '../types/Storage.d';
|
||||||
// We define a stricter storage here that returns `unknown` instead of `any`.
|
|
||||||
type Storage = {
|
|
||||||
get(key: string): unknown;
|
|
||||||
put(key: string, value: unknown): Promise<void>;
|
|
||||||
remove(key: string): Promise<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
function isWellFormed(data: unknown): data is SerializedCertificateType {
|
function isWellFormed(data: unknown): data is SerializedCertificateType {
|
||||||
return serializedCertificateSchema.safeParse(data).success;
|
return serializedCertificateSchema.safeParse(data).success;
|
||||||
|
@ -43,7 +37,7 @@ export class SenderCertificateService {
|
||||||
|
|
||||||
private onlineEventTarget?: EventTarget;
|
private onlineEventTarget?: EventTarget;
|
||||||
|
|
||||||
private storage?: Storage;
|
private storage?: StorageInterface;
|
||||||
|
|
||||||
initialize({
|
initialize({
|
||||||
SenderCertificate,
|
SenderCertificate,
|
||||||
|
@ -56,7 +50,7 @@ export class SenderCertificateService {
|
||||||
navigator: Readonly<{ onLine: boolean }>;
|
navigator: Readonly<{ onLine: boolean }>;
|
||||||
onlineEventTarget: EventTarget;
|
onlineEventTarget: EventTarget;
|
||||||
SenderCertificate: typeof SenderCertificateClass;
|
SenderCertificate: typeof SenderCertificateClass;
|
||||||
storage: Storage;
|
storage: StorageInterface;
|
||||||
}): void {
|
}): void {
|
||||||
log.info('Sender certificate service initialized');
|
log.info('Sender certificate service initialized');
|
||||||
|
|
||||||
|
|
|
@ -91,6 +91,9 @@ async function encryptRecord(
|
||||||
: generateStorageID();
|
: generateStorageID();
|
||||||
|
|
||||||
const storageKeyBase64 = window.storage.get('storageKey');
|
const storageKeyBase64 = window.storage.get('storageKey');
|
||||||
|
if (!storageKeyBase64) {
|
||||||
|
throw new Error('No storage key');
|
||||||
|
}
|
||||||
const storageKey = base64ToArrayBuffer(storageKeyBase64);
|
const storageKey = base64ToArrayBuffer(storageKeyBase64);
|
||||||
const storageItemKey = await deriveStorageItemKey(
|
const storageItemKey = await deriveStorageItemKey(
|
||||||
storageKey,
|
storageKey,
|
||||||
|
@ -260,8 +263,10 @@ async function generateManifest(
|
||||||
manifestRecordKeys.add(identifier);
|
manifestRecordKeys.add(identifier);
|
||||||
});
|
});
|
||||||
|
|
||||||
const recordsWithErrors: ReadonlyArray<UnknownRecord> =
|
const recordsWithErrors: ReadonlyArray<UnknownRecord> = window.storage.get(
|
||||||
window.storage.get('storage-service-error-records') || [];
|
'storage-service-error-records',
|
||||||
|
new Array<UnknownRecord>()
|
||||||
|
);
|
||||||
|
|
||||||
window.log.info(
|
window.log.info(
|
||||||
'storageService.generateManifest: adding records that had errors in the previous merge',
|
'storageService.generateManifest: adding records that had errors in the previous merge',
|
||||||
|
@ -406,6 +411,9 @@ async function generateManifest(
|
||||||
manifestRecord.keys = Array.from(manifestRecordKeys);
|
manifestRecord.keys = Array.from(manifestRecordKeys);
|
||||||
|
|
||||||
const storageKeyBase64 = window.storage.get('storageKey');
|
const storageKeyBase64 = window.storage.get('storageKey');
|
||||||
|
if (!storageKeyBase64) {
|
||||||
|
throw new Error('No storage key');
|
||||||
|
}
|
||||||
const storageKey = base64ToArrayBuffer(storageKeyBase64);
|
const storageKey = base64ToArrayBuffer(storageKeyBase64);
|
||||||
const storageManifestKey = await deriveStorageManifestKey(
|
const storageManifestKey = await deriveStorageManifestKey(
|
||||||
storageKey,
|
storageKey,
|
||||||
|
@ -539,7 +547,7 @@ async function stopStorageServiceSync() {
|
||||||
async function createNewManifest() {
|
async function createNewManifest() {
|
||||||
window.log.info('storageService.createNewManifest: creating new manifest');
|
window.log.info('storageService.createNewManifest: creating new manifest');
|
||||||
|
|
||||||
const version = window.storage.get('manifestVersion') || 0;
|
const version = window.storage.get('manifestVersion', 0);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
conversationsToUpdate,
|
conversationsToUpdate,
|
||||||
|
@ -562,6 +570,9 @@ async function decryptManifest(
|
||||||
const { version, value } = encryptedManifest;
|
const { version, value } = encryptedManifest;
|
||||||
|
|
||||||
const storageKeyBase64 = window.storage.get('storageKey');
|
const storageKeyBase64 = window.storage.get('storageKey');
|
||||||
|
if (!storageKeyBase64) {
|
||||||
|
throw new Error('No storage key');
|
||||||
|
}
|
||||||
const storageKey = base64ToArrayBuffer(storageKeyBase64);
|
const storageKey = base64ToArrayBuffer(storageKeyBase64);
|
||||||
const storageManifestKey = await deriveStorageManifestKey(
|
const storageManifestKey = await deriveStorageManifestKey(
|
||||||
storageKey,
|
storageKey,
|
||||||
|
@ -577,7 +588,7 @@ async function decryptManifest(
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchManifest(
|
async function fetchManifest(
|
||||||
manifestVersion: string
|
manifestVersion: number
|
||||||
): Promise<ManifestRecordClass | undefined> {
|
): Promise<ManifestRecordClass | undefined> {
|
||||||
window.log.info('storageService.fetchManifest');
|
window.log.info('storageService.fetchManifest');
|
||||||
|
|
||||||
|
@ -799,6 +810,9 @@ async function processRemoteRecords(
|
||||||
remoteOnlyRecords: Map<string, RemoteRecord>
|
remoteOnlyRecords: Map<string, RemoteRecord>
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
const storageKeyBase64 = window.storage.get('storageKey');
|
const storageKeyBase64 = window.storage.get('storageKey');
|
||||||
|
if (!storageKeyBase64) {
|
||||||
|
throw new Error('No storage key');
|
||||||
|
}
|
||||||
const storageKey = base64ToArrayBuffer(storageKeyBase64);
|
const storageKey = base64ToArrayBuffer(storageKeyBase64);
|
||||||
|
|
||||||
window.log.info(
|
window.log.info(
|
||||||
|
@ -911,8 +925,10 @@ async function processRemoteRecords(
|
||||||
// Collect full map of previously and currently unknown records
|
// Collect full map of previously and currently unknown records
|
||||||
const unknownRecords: Map<string, UnknownRecord> = new Map();
|
const unknownRecords: Map<string, UnknownRecord> = new Map();
|
||||||
|
|
||||||
const unknownRecordsArray: ReadonlyArray<UnknownRecord> =
|
const unknownRecordsArray: ReadonlyArray<UnknownRecord> = window.storage.get(
|
||||||
window.storage.get('storage-service-unknown-records') || [];
|
'storage-service-unknown-records',
|
||||||
|
new Array<UnknownRecord>()
|
||||||
|
);
|
||||||
unknownRecordsArray.forEach((record: UnknownRecord) => {
|
unknownRecordsArray.forEach((record: UnknownRecord) => {
|
||||||
unknownRecords.set(record.storageID, record);
|
unknownRecords.set(record.storageID, record);
|
||||||
});
|
});
|
||||||
|
@ -1087,7 +1103,7 @@ async function upload(fromSync = false): Promise<void> {
|
||||||
previousManifest = await sync();
|
previousManifest = await sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
const localManifestVersion = window.storage.get('manifestVersion') || 0;
|
const localManifestVersion = window.storage.get('manifestVersion', 0);
|
||||||
const version = Number(localManifestVersion) + 1;
|
const version = Number(localManifestVersion) + 1;
|
||||||
|
|
||||||
window.log.info(
|
window.log.info(
|
||||||
|
|
|
@ -229,7 +229,7 @@ export async function toAccountRecord(
|
||||||
}
|
}
|
||||||
|
|
||||||
const pinnedConversations = window.storage
|
const pinnedConversations = window.storage
|
||||||
.get<Array<string>>('pinnedConversationIds', [])
|
.get('pinnedConversationIds', new Array<string>())
|
||||||
.map(id => {
|
.map(id => {
|
||||||
const pinnedConversation = window.ConversationController.get(id);
|
const pinnedConversation = window.ConversationController.get(id);
|
||||||
|
|
||||||
|
@ -824,7 +824,7 @@ export async function mergeAccountRecord(
|
||||||
universalExpireTimer,
|
universalExpireTimer,
|
||||||
} = accountRecord;
|
} = accountRecord;
|
||||||
|
|
||||||
window.storage.put('read-receipt-setting', readReceipts);
|
window.storage.put('read-receipt-setting', Boolean(readReceipts));
|
||||||
|
|
||||||
if (typeof sealedSenderIndicators === 'boolean') {
|
if (typeof sealedSenderIndicators === 'boolean') {
|
||||||
window.storage.put('sealedSenderIndicators', sealedSenderIndicators);
|
window.storage.put('sealedSenderIndicators', sealedSenderIndicators);
|
||||||
|
@ -890,7 +890,7 @@ export async function mergeAccountRecord(
|
||||||
);
|
);
|
||||||
|
|
||||||
const missingStoragePinnedConversationIds = window.storage
|
const missingStoragePinnedConversationIds = window.storage
|
||||||
.get<Array<string>>('pinnedConversationIds', [])
|
.get('pinnedConversationIds', new Array<string>())
|
||||||
.filter(id => !modelPinnedConversationIds.includes(id));
|
.filter(id => !modelPinnedConversationIds.includes(id));
|
||||||
|
|
||||||
if (missingStoragePinnedConversationIds.length !== 0) {
|
if (missingStoragePinnedConversationIds.length !== 0) {
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
// Copyright 2019-2020 Signal Messenger, LLC
|
// Copyright 2019-2020 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { StorageAccessType } from '../types/Storage.d';
|
||||||
|
|
||||||
// Matching window.storage.put API
|
// Matching window.storage.put API
|
||||||
// eslint-disable-next-line max-len
|
export function put<K extends keyof StorageAccessType>(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
|
key: K,
|
||||||
export function put(key: string, value: any): void {
|
value: StorageAccessType[K]
|
||||||
|
): void {
|
||||||
window.storage.put(key, value);
|
window.storage.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function remove(key: string): Promise<void> {
|
export async function remove(key: keyof StorageAccessType): Promise<void> {
|
||||||
await window.storage.remove(key);
|
await window.storage.remove(key);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* eslint-disable @typescript-eslint/ban-types */
|
/* eslint-disable @typescript-eslint/ban-types */
|
||||||
|
/* eslint-disable no-restricted-syntax */
|
||||||
import { ipcRenderer } from 'electron';
|
import { ipcRenderer } from 'electron';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -44,6 +45,7 @@ import {
|
||||||
ClientJobType,
|
ClientJobType,
|
||||||
ConversationType,
|
ConversationType,
|
||||||
IdentityKeyType,
|
IdentityKeyType,
|
||||||
|
ItemKeyType,
|
||||||
ItemType,
|
ItemType,
|
||||||
MessageType,
|
MessageType,
|
||||||
MessageTypeUnhydrated,
|
MessageTypeUnhydrated,
|
||||||
|
@ -132,7 +134,6 @@ const dataInterface: ClientInterface = {
|
||||||
createOrUpdateItem,
|
createOrUpdateItem,
|
||||||
getItemById,
|
getItemById,
|
||||||
getAllItems,
|
getAllItems,
|
||||||
bulkAddItems,
|
|
||||||
removeItemById,
|
removeItemById,
|
||||||
removeAllItems,
|
removeAllItems,
|
||||||
|
|
||||||
|
@ -692,14 +693,14 @@ async function removeAllSignedPreKeys() {
|
||||||
|
|
||||||
// Items
|
// Items
|
||||||
|
|
||||||
const ITEM_KEYS: { [key: string]: Array<string> | undefined } = {
|
const ITEM_KEYS: Partial<Record<ItemKeyType, Array<string>>> = {
|
||||||
identityKey: ['value.pubKey', 'value.privKey'],
|
identityKey: ['value.pubKey', 'value.privKey'],
|
||||||
senderCertificate: ['value.serialized'],
|
senderCertificate: ['value.serialized'],
|
||||||
senderCertificateNoE164: ['value.serialized'],
|
senderCertificateNoE164: ['value.serialized'],
|
||||||
signaling_key: ['value'],
|
signaling_key: ['value'],
|
||||||
profileKey: ['value'],
|
profileKey: ['value'],
|
||||||
};
|
};
|
||||||
async function createOrUpdateItem(data: ItemType) {
|
async function createOrUpdateItem<K extends ItemKeyType>(data: ItemType<K>) {
|
||||||
const { id } = data;
|
const { id } = data;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -712,7 +713,7 @@ async function createOrUpdateItem(data: ItemType) {
|
||||||
|
|
||||||
await channels.createOrUpdateItem(updated);
|
await channels.createOrUpdateItem(updated);
|
||||||
}
|
}
|
||||||
async function getItemById(id: string) {
|
async function getItemById<K extends ItemKeyType>(id: K): Promise<ItemType<K>> {
|
||||||
const keys = ITEM_KEYS[id];
|
const keys = ITEM_KEYS[id];
|
||||||
const data = await channels.getItemById(id);
|
const data = await channels.getItemById(id);
|
||||||
|
|
||||||
|
@ -721,23 +722,24 @@ async function getItemById(id: string) {
|
||||||
async function getAllItems() {
|
async function getAllItems() {
|
||||||
const items = await channels.getAllItems();
|
const items = await channels.getAllItems();
|
||||||
|
|
||||||
return map(items, item => {
|
const result = Object.create(null);
|
||||||
const { id } = item;
|
|
||||||
const keys = ITEM_KEYS[id];
|
|
||||||
|
|
||||||
return Array.isArray(keys) ? keysToArrayBuffer(keys, item) : item;
|
for (const id of Object.keys(items)) {
|
||||||
});
|
const key = id as ItemKeyType;
|
||||||
}
|
const value = items[key];
|
||||||
async function bulkAddItems(array: Array<ItemType>) {
|
|
||||||
const updated = map(array, data => {
|
|
||||||
const { id } = data;
|
|
||||||
const keys = ITEM_KEYS[id];
|
|
||||||
|
|
||||||
return keys && Array.isArray(keys) ? keysFromArrayBuffer(keys, data) : data;
|
const keys = ITEM_KEYS[key];
|
||||||
});
|
|
||||||
await channels.bulkAddItems(updated);
|
const deserializedValue = Array.isArray(keys)
|
||||||
|
? keysToArrayBuffer(keys, { value }).value
|
||||||
|
: value;
|
||||||
|
|
||||||
|
result[key] = deserializedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
async function removeItemById(id: string) {
|
async function removeItemById(id: ItemKeyType) {
|
||||||
await channels.removeItemById(id);
|
await channels.removeItemById(id);
|
||||||
}
|
}
|
||||||
async function removeAllItems() {
|
async function removeAllItems() {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { ConversationModel } from '../models/conversations';
|
||||||
import { StoredJob } from '../jobs/types';
|
import { StoredJob } from '../jobs/types';
|
||||||
import { ReactionType } from '../types/Reactions';
|
import { ReactionType } from '../types/Reactions';
|
||||||
import { ConversationColorType, CustomColorType } from '../types/Colors';
|
import { ConversationColorType, CustomColorType } from '../types/Colors';
|
||||||
|
import { StorageAccessType } from '../types/Storage.d';
|
||||||
|
|
||||||
export type AttachmentDownloadJobType = {
|
export type AttachmentDownloadJobType = {
|
||||||
id: string;
|
id: string;
|
||||||
|
@ -48,7 +49,12 @@ export type IdentityKeyType = {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
verified: number;
|
verified: number;
|
||||||
};
|
};
|
||||||
export type ItemType = any;
|
export type ItemKeyType = keyof StorageAccessType;
|
||||||
|
export type AllItemsType = Partial<StorageAccessType>;
|
||||||
|
export type ItemType<K extends ItemKeyType> = {
|
||||||
|
id: K;
|
||||||
|
value: StorageAccessType[K];
|
||||||
|
};
|
||||||
export type MessageType = MessageAttributesType;
|
export type MessageType = MessageAttributesType;
|
||||||
export type MessageTypeUnhydrated = {
|
export type MessageTypeUnhydrated = {
|
||||||
json: string;
|
json: string;
|
||||||
|
@ -177,12 +183,11 @@ export type DataInterface = {
|
||||||
removeAllSignedPreKeys: () => Promise<void>;
|
removeAllSignedPreKeys: () => Promise<void>;
|
||||||
getAllSignedPreKeys: () => Promise<Array<SignedPreKeyType>>;
|
getAllSignedPreKeys: () => Promise<Array<SignedPreKeyType>>;
|
||||||
|
|
||||||
createOrUpdateItem: (data: ItemType) => Promise<void>;
|
createOrUpdateItem<K extends ItemKeyType>(data: ItemType<K>): Promise<void>;
|
||||||
getItemById: (id: string) => Promise<ItemType | undefined>;
|
getItemById<K extends ItemKeyType>(id: K): Promise<ItemType<K> | undefined>;
|
||||||
bulkAddItems: (array: Array<ItemType>) => Promise<void>;
|
removeItemById: (id: ItemKeyType) => Promise<void>;
|
||||||
removeItemById: (id: string) => Promise<void>;
|
|
||||||
removeAllItems: () => Promise<void>;
|
removeAllItems: () => Promise<void>;
|
||||||
getAllItems: () => Promise<Array<ItemType>>;
|
getAllItems: () => Promise<AllItemsType>;
|
||||||
|
|
||||||
createOrUpdateSenderKey: (key: SenderKeyType) => Promise<void>;
|
createOrUpdateSenderKey: (key: SenderKeyType) => Promise<void>;
|
||||||
getSenderKeyById: (id: string) => Promise<SenderKeyType | undefined>;
|
getSenderKeyById: (id: string) => Promise<SenderKeyType | undefined>;
|
||||||
|
|
|
@ -44,6 +44,8 @@ import {
|
||||||
ConversationType,
|
ConversationType,
|
||||||
EmojiType,
|
EmojiType,
|
||||||
IdentityKeyType,
|
IdentityKeyType,
|
||||||
|
AllItemsType,
|
||||||
|
ItemKeyType,
|
||||||
ItemType,
|
ItemType,
|
||||||
MessageType,
|
MessageType,
|
||||||
MessageTypeUnhydrated,
|
MessageTypeUnhydrated,
|
||||||
|
@ -123,7 +125,6 @@ const dataInterface: ServerInterface = {
|
||||||
createOrUpdateItem,
|
createOrUpdateItem,
|
||||||
getItemById,
|
getItemById,
|
||||||
getAllItems,
|
getAllItems,
|
||||||
bulkAddItems,
|
|
||||||
removeItemById,
|
removeItemById,
|
||||||
removeAllItems,
|
removeAllItems,
|
||||||
|
|
||||||
|
@ -2170,24 +2171,34 @@ async function getAllSignedPreKeys(): Promise<Array<SignedPreKeyType>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ITEMS_TABLE = 'items';
|
const ITEMS_TABLE = 'items';
|
||||||
function createOrUpdateItem(data: ItemType): Promise<void> {
|
function createOrUpdateItem<K extends ItemKeyType>(
|
||||||
|
data: ItemType<K>
|
||||||
|
): Promise<void> {
|
||||||
return createOrUpdate(ITEMS_TABLE, data);
|
return createOrUpdate(ITEMS_TABLE, data);
|
||||||
}
|
}
|
||||||
function getItemById(id: string): Promise<ItemType> {
|
function getItemById<K extends ItemKeyType>(
|
||||||
|
id: K
|
||||||
|
): Promise<ItemType<K> | undefined> {
|
||||||
return getById(ITEMS_TABLE, id);
|
return getById(ITEMS_TABLE, id);
|
||||||
}
|
}
|
||||||
async function getAllItems(): Promise<Array<ItemType>> {
|
async function getAllItems(): Promise<AllItemsType> {
|
||||||
const db = getInstance();
|
const db = getInstance();
|
||||||
const rows: JSONRows = db
|
const rows: JSONRows = db
|
||||||
.prepare<EmptyQuery>('SELECT json FROM items ORDER BY id ASC;')
|
.prepare<EmptyQuery>('SELECT json FROM items ORDER BY id ASC;')
|
||||||
.all();
|
.all();
|
||||||
|
|
||||||
return rows.map(row => jsonToObject(row.json));
|
const items = rows.map(row => jsonToObject(row.json));
|
||||||
|
|
||||||
|
const result: AllItemsType = Object.create(null);
|
||||||
|
|
||||||
|
for (const { id, value } of items) {
|
||||||
|
const key = id as ItemKeyType;
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
function bulkAddItems(array: Array<ItemType>): Promise<void> {
|
function removeItemById(id: ItemKeyType): Promise<void> {
|
||||||
return bulkAdd(ITEMS_TABLE, array);
|
|
||||||
}
|
|
||||||
function removeItemById(id: string): Promise<void> {
|
|
||||||
return removeById(ITEMS_TABLE, id);
|
return removeById(ITEMS_TABLE, id);
|
||||||
}
|
}
|
||||||
function removeAllItems(): Promise<void> {
|
function removeAllItems(): Promise<void> {
|
||||||
|
|
|
@ -11,9 +11,11 @@ import {
|
||||||
ConversationColors,
|
ConversationColors,
|
||||||
ConversationColorType,
|
ConversationColorType,
|
||||||
CustomColorType,
|
CustomColorType,
|
||||||
|
CustomColorsItemType,
|
||||||
DefaultConversationColorType,
|
DefaultConversationColorType,
|
||||||
} from '../../types/Colors';
|
} from '../../types/Colors';
|
||||||
import { reloadSelectedConversation } from '../../shims/reloadSelectedConversation';
|
import { reloadSelectedConversation } from '../../shims/reloadSelectedConversation';
|
||||||
|
import { StorageAccessType } from '../../types/Storage.d';
|
||||||
|
|
||||||
// State
|
// State
|
||||||
|
|
||||||
|
@ -25,10 +27,7 @@ export type ItemsStateType = {
|
||||||
// This property should always be set and this is ensured in background.ts
|
// This property should always be set and this is ensured in background.ts
|
||||||
readonly defaultConversationColor?: DefaultConversationColorType;
|
readonly defaultConversationColor?: DefaultConversationColorType;
|
||||||
|
|
||||||
readonly customColors?: {
|
readonly customColors?: CustomColorsItemType;
|
||||||
readonly colors: Record<string, CustomColorType>;
|
|
||||||
readonly version: number;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
@ -85,7 +84,10 @@ export const actions = {
|
||||||
|
|
||||||
export const useActions = (): typeof actions => useBoundActions(actions);
|
export const useActions = (): typeof actions => useBoundActions(actions);
|
||||||
|
|
||||||
function putItem(key: string, value: unknown): ItemPutAction {
|
function putItem<K extends keyof StorageAccessType>(
|
||||||
|
key: K,
|
||||||
|
value: StorageAccessType[K]
|
||||||
|
): ItemPutAction {
|
||||||
storageShim.put(key, value);
|
storageShim.put(key, value);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -108,7 +110,7 @@ function putItemExternal(key: string, value: unknown): ItemPutExternalAction {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeItem(key: string): ItemRemoveAction {
|
function removeItem(key: keyof StorageAccessType): ItemRemoveAction {
|
||||||
storageShim.remove(key);
|
storageShim.remove(key);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -118,10 +118,10 @@ describe('ChallengeHandler', () => {
|
||||||
expireAfter,
|
expireAfter,
|
||||||
|
|
||||||
storage: {
|
storage: {
|
||||||
get(key) {
|
get(key: string) {
|
||||||
return storage.get(key);
|
return storage.get(key);
|
||||||
},
|
},
|
||||||
async put(key, value) {
|
async put(key: string, value: unknown) {
|
||||||
storage.set(key, value);
|
storage.set(key, value);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
import { assert } from 'chai';
|
import { assert } from 'chai';
|
||||||
import { size } from '../../util/iterables';
|
import { size } from '../../util/iterables';
|
||||||
|
|
||||||
|
import { typedArrayToArrayBuffer } from '../../Crypto';
|
||||||
|
|
||||||
import { getProvisioningUrl } from '../../util/getProvisioningUrl';
|
import { getProvisioningUrl } from '../../util/getProvisioningUrl';
|
||||||
|
|
||||||
describe('getProvisioningUrl', () => {
|
describe('getProvisioningUrl', () => {
|
||||||
|
@ -11,7 +13,7 @@ describe('getProvisioningUrl', () => {
|
||||||
const uuid = 'a08bf1fd-1799-427f-a551-70af747e3956';
|
const uuid = 'a08bf1fd-1799-427f-a551-70af747e3956';
|
||||||
const publicKey = new Uint8Array([9, 8, 7, 6, 5, 4, 3]);
|
const publicKey = new Uint8Array([9, 8, 7, 6, 5, 4, 3]);
|
||||||
|
|
||||||
const result = getProvisioningUrl(uuid, publicKey);
|
const result = getProvisioningUrl(uuid, typedArrayToArrayBuffer(publicKey));
|
||||||
const resultUrl = new URL(result);
|
const resultUrl = new URL(result);
|
||||||
|
|
||||||
assert(result.startsWith('tsdevice:/?'));
|
assert(result.startsWith('tsdevice:/?'));
|
||||||
|
|
|
@ -18,7 +18,7 @@ describe('RetryPlaceholders', () => {
|
||||||
let clock: any;
|
let clock: any;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
window.storage.put(STORAGE_KEY, null);
|
window.storage.put(STORAGE_KEY, undefined as any);
|
||||||
|
|
||||||
clock = sinon.useFakeTimers({
|
clock = sinon.useFakeTimers({
|
||||||
now: NOW,
|
now: NOW,
|
||||||
|
@ -55,7 +55,7 @@ describe('RetryPlaceholders', () => {
|
||||||
window.storage.put(STORAGE_KEY, [
|
window.storage.put(STORAGE_KEY, [
|
||||||
{ item: 'is wrong shape!' },
|
{ item: 'is wrong shape!' },
|
||||||
{ bad: 'is not good!' },
|
{ bad: 'is not good!' },
|
||||||
]);
|
] as any);
|
||||||
|
|
||||||
const placeholders = new RetryPlaceholders();
|
const placeholders = new RetryPlaceholders();
|
||||||
|
|
||||||
|
|
|
@ -44,9 +44,9 @@ describe('synchronousCrypto', () => {
|
||||||
|
|
||||||
describe('encrypt+decrypt', () => {
|
describe('encrypt+decrypt', () => {
|
||||||
it('returns original input', () => {
|
it('returns original input', () => {
|
||||||
const iv = crypto.randomBytes(16);
|
const iv = toArrayBuffer(crypto.randomBytes(16));
|
||||||
const key = crypto.randomBytes(32);
|
const key = toArrayBuffer(crypto.randomBytes(32));
|
||||||
const input = Buffer.from('plaintext');
|
const input = toArrayBuffer(Buffer.from('plaintext'));
|
||||||
|
|
||||||
const ciphertext = encrypt(key, input, iv);
|
const ciphertext = encrypt(key, input, iv);
|
||||||
const plaintext = decrypt(key, ciphertext, iv);
|
const plaintext = decrypt(key, ciphertext, iv);
|
||||||
|
|
|
@ -15,7 +15,11 @@ import { signal } from '../protobuf/compiled';
|
||||||
import { sessionStructureToArrayBuffer } from '../util/sessionTranslation';
|
import { sessionStructureToArrayBuffer } from '../util/sessionTranslation';
|
||||||
import { Zone } from '../util/Zone';
|
import { Zone } from '../util/Zone';
|
||||||
|
|
||||||
import { getRandomBytes, constantTimeEqual } from '../Crypto';
|
import {
|
||||||
|
getRandomBytes,
|
||||||
|
constantTimeEqual,
|
||||||
|
typedArrayToArrayBuffer as toArrayBuffer,
|
||||||
|
} from '../Crypto';
|
||||||
import { clampPrivateKey, setPublicKeyTypeByte } from '../Curve';
|
import { clampPrivateKey, setPublicKeyTypeByte } from '../Curve';
|
||||||
import { SignalProtocolStore, GLOBAL_ZONE } from '../SignalProtocolStore';
|
import { SignalProtocolStore, GLOBAL_ZONE } from '../SignalProtocolStore';
|
||||||
import { IdentityKeyType, KeyPairType } from '../textsecure/Types.d';
|
import { IdentityKeyType, KeyPairType } from '../textsecure/Types.d';
|
||||||
|
@ -173,7 +177,10 @@ describe('SignalProtocolStore', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.isTrue(
|
assert.isTrue(
|
||||||
constantTimeEqual(expected.serialize(), actual.serialize())
|
constantTimeEqual(
|
||||||
|
toArrayBuffer(expected.serialize()),
|
||||||
|
toArrayBuffer(actual.serialize())
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
await store.removeSenderKey(encodedAddress, distributionId);
|
await store.removeSenderKey(encodedAddress, distributionId);
|
||||||
|
@ -203,7 +210,10 @@ describe('SignalProtocolStore', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.isTrue(
|
assert.isTrue(
|
||||||
constantTimeEqual(expected.serialize(), actual.serialize())
|
constantTimeEqual(
|
||||||
|
toArrayBuffer(expected.serialize()),
|
||||||
|
toArrayBuffer(actual.serialize())
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
await store.removeSenderKey(encodedAddress, distributionId);
|
await store.removeSenderKey(encodedAddress, distributionId);
|
||||||
|
|
|
@ -12,6 +12,8 @@ import * as sinon from 'sinon';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
import { connection as WebSocket } from 'websocket';
|
import { connection as WebSocket } from 'websocket';
|
||||||
|
|
||||||
|
import { typedArrayToArrayBuffer as toArrayBuffer } from '../Crypto';
|
||||||
|
|
||||||
import WebSocketResource from '../textsecure/WebsocketResources';
|
import WebSocketResource from '../textsecure/WebsocketResources';
|
||||||
|
|
||||||
describe('WebSocket-Resource', () => {
|
describe('WebSocket-Resource', () => {
|
||||||
|
@ -29,7 +31,7 @@ describe('WebSocket-Resource', () => {
|
||||||
|
|
||||||
sinon.stub(socket, 'sendBytes').callsFake((data: Uint8Array) => {
|
sinon.stub(socket, 'sendBytes').callsFake((data: Uint8Array) => {
|
||||||
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
||||||
data
|
toArrayBuffer(data)
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
message.type,
|
message.type,
|
||||||
|
@ -86,7 +88,7 @@ describe('WebSocket-Resource', () => {
|
||||||
|
|
||||||
sinon.stub(socket, 'sendBytes').callsFake((data: Uint8Array) => {
|
sinon.stub(socket, 'sendBytes').callsFake((data: Uint8Array) => {
|
||||||
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
||||||
data
|
toArrayBuffer(data)
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
message.type,
|
message.type,
|
||||||
|
@ -166,7 +168,7 @@ describe('WebSocket-Resource', () => {
|
||||||
|
|
||||||
sinon.stub(socket, 'sendBytes').callsFake(data => {
|
sinon.stub(socket, 'sendBytes').callsFake(data => {
|
||||||
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
||||||
data
|
toArrayBuffer(data)
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
message.type,
|
message.type,
|
||||||
|
@ -189,7 +191,7 @@ describe('WebSocket-Resource', () => {
|
||||||
|
|
||||||
sinon.stub(socket, 'sendBytes').callsFake(data => {
|
sinon.stub(socket, 'sendBytes').callsFake(data => {
|
||||||
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
||||||
data
|
toArrayBuffer(data)
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
message.type,
|
message.type,
|
||||||
|
@ -230,7 +232,7 @@ describe('WebSocket-Resource', () => {
|
||||||
|
|
||||||
sinon.stub(socket, 'sendBytes').callsFake(data => {
|
sinon.stub(socket, 'sendBytes').callsFake(data => {
|
||||||
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
||||||
data
|
toArrayBuffer(data)
|
||||||
);
|
);
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
message.type,
|
message.type,
|
||||||
|
|
|
@ -44,14 +44,15 @@ describe('#cleanupSessionResets', () => {
|
||||||
it('filters out falsey items', () => {
|
it('filters out falsey items', () => {
|
||||||
const startValue = {
|
const startValue = {
|
||||||
one: 0,
|
one: 0,
|
||||||
two: false,
|
two: Date.now(),
|
||||||
three: Date.now(),
|
|
||||||
};
|
};
|
||||||
window.storage.put('sessionResets', startValue);
|
window.storage.put('sessionResets', startValue);
|
||||||
cleanupSessionResets();
|
cleanupSessionResets();
|
||||||
const actual = window.storage.get('sessionResets');
|
const actual = window.storage.get('sessionResets');
|
||||||
|
|
||||||
const expected = window._.pick(startValue, ['three']);
|
const expected = window._.pick(startValue, ['two']);
|
||||||
assert.deepEqual(actual, expected);
|
assert.deepEqual(actual, expected);
|
||||||
|
|
||||||
|
assert.deepEqual(Object.keys(startValue), ['two']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,8 +37,8 @@ describe('Message', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
after(async () => {
|
after(async () => {
|
||||||
window.textsecure.storage.put('number_id', null);
|
window.textsecure.storage.remove('number_id');
|
||||||
window.textsecure.storage.put('uuid_id', null);
|
window.textsecure.storage.remove('uuid_id');
|
||||||
|
|
||||||
await window.Signal.Data.removeAll();
|
await window.Signal.Data.removeAll();
|
||||||
await window.storage.fetch();
|
await window.storage.fetch();
|
||||||
|
|
|
@ -66,7 +66,7 @@ describe('routineProfileRefresh', () => {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeStorage(lastAttemptAt: undefined | number = undefined) {
|
function makeStorage(lastAttemptAt?: number) {
|
||||||
return {
|
return {
|
||||||
get: sinonSandbox
|
get: sinonSandbox
|
||||||
.stub()
|
.stub()
|
||||||
|
|
45
ts/textsecure.d.ts
vendored
45
ts/textsecure.d.ts
vendored
|
@ -14,7 +14,11 @@ import { WebAPIType } from './textsecure/WebAPI';
|
||||||
import utils from './textsecure/Helpers';
|
import utils from './textsecure/Helpers';
|
||||||
import { CallingMessage as CallingMessageClass } from 'ringrtc';
|
import { CallingMessage as CallingMessageClass } from 'ringrtc';
|
||||||
import { WhatIsThis } from './window.d';
|
import { WhatIsThis } from './window.d';
|
||||||
import { SignalProtocolStore } from './SignalProtocolStore';
|
import { Storage } from './textsecure/Storage';
|
||||||
|
import {
|
||||||
|
StorageServiceCallOptionsType,
|
||||||
|
StorageServiceCredentials,
|
||||||
|
} from './textsecure/Types.d';
|
||||||
|
|
||||||
export type UnprocessedType = {
|
export type UnprocessedType = {
|
||||||
attempts: number;
|
attempts: number;
|
||||||
|
@ -30,15 +34,7 @@ export type UnprocessedType = {
|
||||||
version: number;
|
version: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StorageServiceCallOptionsType = {
|
export { StorageServiceCallOptionsType, StorageServiceCredentials };
|
||||||
credentials?: StorageServiceCredentials;
|
|
||||||
greaterThanVersion?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type StorageServiceCredentials = {
|
|
||||||
username: string;
|
|
||||||
password: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TextSecureType = {
|
export type TextSecureType = {
|
||||||
createTaskWithTimeout: (
|
createTaskWithTimeout: (
|
||||||
|
@ -47,34 +43,7 @@ export type TextSecureType = {
|
||||||
options?: { timeout?: number }
|
options?: { timeout?: number }
|
||||||
) => () => Promise<any>;
|
) => () => Promise<any>;
|
||||||
crypto: typeof Crypto;
|
crypto: typeof Crypto;
|
||||||
storage: {
|
storage: Storage;
|
||||||
user: {
|
|
||||||
getNumber: () => string;
|
|
||||||
getUuid: () => string | undefined;
|
|
||||||
getDeviceId: () => number | string;
|
|
||||||
getDeviceName: () => string;
|
|
||||||
getDeviceNameEncrypted: () => boolean;
|
|
||||||
setDeviceNameEncrypted: () => Promise<void>;
|
|
||||||
getSignalingKey: () => ArrayBuffer;
|
|
||||||
setNumberAndDeviceId: (
|
|
||||||
number: string,
|
|
||||||
deviceId: number,
|
|
||||||
deviceName?: string | null
|
|
||||||
) => Promise<void>;
|
|
||||||
setUuidAndDeviceId: (uuid: string, deviceId: number) => Promise<void>;
|
|
||||||
};
|
|
||||||
unprocessed: {
|
|
||||||
remove: (id: string | Array<string>) => Promise<void>;
|
|
||||||
getCount: () => Promise<number>;
|
|
||||||
removeAll: () => Promise<void>;
|
|
||||||
getAll: () => Promise<Array<UnprocessedType>>;
|
|
||||||
updateAttempts: (id: string, attempts: number) => Promise<void>;
|
|
||||||
};
|
|
||||||
get: (key: string, defaultValue?: any) => any;
|
|
||||||
put: (key: string, value: any) => Promise<void>;
|
|
||||||
remove: (key: string | Array<string>) => Promise<void>;
|
|
||||||
protocol: SignalProtocolStore;
|
|
||||||
};
|
|
||||||
messageReceiver: MessageReceiver;
|
messageReceiver: MessageReceiver;
|
||||||
messageSender: MessageSender;
|
messageSender: MessageSender;
|
||||||
messaging: SendMessage;
|
messaging: SendMessage;
|
||||||
|
|
|
@ -36,7 +36,7 @@ const PREKEY_ROTATION_AGE = 24 * 60 * 60 * 1000;
|
||||||
const PROFILE_KEY_LENGTH = 32;
|
const PROFILE_KEY_LENGTH = 32;
|
||||||
const SIGNED_KEY_GEN_BATCH_SIZE = 100;
|
const SIGNED_KEY_GEN_BATCH_SIZE = 100;
|
||||||
|
|
||||||
function getIdentifier(id: string) {
|
function getIdentifier(id: string | undefined) {
|
||||||
if (!id || !id.length) {
|
if (!id || !id.length) {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ export default class AccountManager extends EventTarget {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const deviceName = window.textsecure.storage.user.getDeviceName();
|
const deviceName = window.textsecure.storage.user.getDeviceName();
|
||||||
const base64 = await this.encryptDeviceName(deviceName);
|
const base64 = await this.encryptDeviceName(deviceName || '');
|
||||||
|
|
||||||
if (base64) {
|
if (base64) {
|
||||||
await this.server.updateDeviceName(base64);
|
await this.server.updateDeviceName(base64);
|
||||||
|
@ -578,7 +578,7 @@ export default class AccountManager extends EventTarget {
|
||||||
window.textsecure.storage.remove('regionCode'),
|
window.textsecure.storage.remove('regionCode'),
|
||||||
window.textsecure.storage.remove('userAgent'),
|
window.textsecure.storage.remove('userAgent'),
|
||||||
window.textsecure.storage.remove('profileKey'),
|
window.textsecure.storage.remove('profileKey'),
|
||||||
window.textsecure.storage.remove('read-receipts-setting'),
|
window.textsecure.storage.remove('read-receipt-setting'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// `setNumberAndDeviceId` and `setUuidAndDeviceId` need to be called
|
// `setNumberAndDeviceId` and `setUuidAndDeviceId` need to be called
|
||||||
|
@ -590,7 +590,7 @@ export default class AccountManager extends EventTarget {
|
||||||
await window.textsecure.storage.user.setNumberAndDeviceId(
|
await window.textsecure.storage.user.setNumberAndDeviceId(
|
||||||
number,
|
number,
|
||||||
response.deviceId || 1,
|
response.deviceId || 1,
|
||||||
deviceName
|
deviceName || undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
if (uuid) {
|
if (uuid) {
|
||||||
|
|
|
@ -756,7 +756,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { id } = item;
|
const { id } = item;
|
||||||
await window.textsecure.storage.unprocessed.remove(id);
|
await window.textsecure.storage.protocol.removeUnprocessed(id);
|
||||||
} catch (deleteError) {
|
} catch (deleteError) {
|
||||||
window.log.error(
|
window.log.error(
|
||||||
'queueCached error deleting item',
|
'queueCached error deleting item',
|
||||||
|
@ -800,17 +800,17 @@ class MessageReceiverInner extends EventTarget {
|
||||||
|
|
||||||
async getAllFromCache() {
|
async getAllFromCache() {
|
||||||
window.log.info('getAllFromCache');
|
window.log.info('getAllFromCache');
|
||||||
const count = await window.textsecure.storage.unprocessed.getCount();
|
const count = await window.textsecure.storage.protocol.getUnprocessedCount();
|
||||||
|
|
||||||
if (count > 1500) {
|
if (count > 1500) {
|
||||||
await window.textsecure.storage.unprocessed.removeAll();
|
await window.textsecure.storage.protocol.removeAllUnprocessed();
|
||||||
window.log.warn(
|
window.log.warn(
|
||||||
`There were ${count} messages in cache. Deleted all instead of reprocessing`
|
`There were ${count} messages in cache. Deleted all instead of reprocessing`
|
||||||
);
|
);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const items = await window.textsecure.storage.unprocessed.getAll();
|
const items = await window.textsecure.storage.protocol.getAllUnprocessed();
|
||||||
window.log.info('getAllFromCache loaded', items.length, 'saved envelopes');
|
window.log.info('getAllFromCache loaded', items.length, 'saved envelopes');
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
|
@ -823,9 +823,9 @@ class MessageReceiverInner extends EventTarget {
|
||||||
'getAllFromCache final attempt for envelope',
|
'getAllFromCache final attempt for envelope',
|
||||||
item.id
|
item.id
|
||||||
);
|
);
|
||||||
await window.textsecure.storage.unprocessed.remove(item.id);
|
await window.textsecure.storage.protocol.removeUnprocessed(item.id);
|
||||||
} else {
|
} else {
|
||||||
await window.textsecure.storage.unprocessed.updateAttempts(
|
await window.textsecure.storage.protocol.updateUnprocessedAttempts(
|
||||||
item.id,
|
item.id,
|
||||||
attempts
|
attempts
|
||||||
);
|
);
|
||||||
|
@ -981,7 +981,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
async cacheRemoveBatch(items: Array<string>) {
|
async cacheRemoveBatch(items: Array<string>) {
|
||||||
await window.textsecure.storage.unprocessed.remove(items);
|
await window.textsecure.storage.protocol.removeUnprocessed(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeFromCache(envelope: EnvelopeClass) {
|
removeFromCache(envelope: EnvelopeClass) {
|
||||||
|
@ -1361,7 +1361,7 @@ class MessageReceiverInner extends EventTarget {
|
||||||
buffer,
|
buffer,
|
||||||
PublicKey.deserialize(Buffer.from(serverTrustRoot)),
|
PublicKey.deserialize(Buffer.from(serverTrustRoot)),
|
||||||
envelope.serverTimestamp,
|
envelope.serverTimestamp,
|
||||||
localE164,
|
localE164 || null,
|
||||||
localUuid,
|
localUuid,
|
||||||
localDeviceId,
|
localDeviceId,
|
||||||
sessionStore,
|
sessionStore,
|
||||||
|
@ -2417,7 +2417,9 @@ class MessageReceiverInner extends EventTarget {
|
||||||
blocked: SyncMessageClass.Blocked
|
blocked: SyncMessageClass.Blocked
|
||||||
) {
|
) {
|
||||||
window.log.info('Setting these numbers as blocked:', blocked.numbers);
|
window.log.info('Setting these numbers as blocked:', blocked.numbers);
|
||||||
|
if (blocked.numbers) {
|
||||||
await window.textsecure.storage.put('blocked', blocked.numbers);
|
await window.textsecure.storage.put('blocked', blocked.numbers);
|
||||||
|
}
|
||||||
if (blocked.uuids) {
|
if (blocked.uuids) {
|
||||||
window.normalizeUuids(
|
window.normalizeUuids(
|
||||||
blocked,
|
blocked,
|
||||||
|
@ -2439,17 +2441,15 @@ class MessageReceiverInner extends EventTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
isBlocked(number: string) {
|
isBlocked(number: string) {
|
||||||
return window.textsecure.storage.get('blocked', []).includes(number);
|
return window.textsecure.storage.blocked.isBlocked(number);
|
||||||
}
|
}
|
||||||
|
|
||||||
isUuidBlocked(uuid: string) {
|
isUuidBlocked(uuid: string) {
|
||||||
return window.textsecure.storage.get('blocked-uuids', []).includes(uuid);
|
return window.textsecure.storage.blocked.isUuidBlocked(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
isGroupBlocked(groupId: string) {
|
isGroupBlocked(groupId: string) {
|
||||||
return window.textsecure.storage
|
return window.textsecure.storage.blocked.isGroupBlocked(groupId);
|
||||||
.get('blocked-groups', [])
|
|
||||||
.includes(groupId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanAttachment(attachment: AttachmentPointerClass) {
|
cleanAttachment(attachment: AttachmentPointerClass) {
|
||||||
|
|
|
@ -39,6 +39,7 @@ import {
|
||||||
} from './Errors';
|
} from './Errors';
|
||||||
import { isValidNumber } from '../types/PhoneNumber';
|
import { isValidNumber } from '../types/PhoneNumber';
|
||||||
import { Sessions, IdentityKeys } from '../LibSignalStores';
|
import { Sessions, IdentityKeys } from '../LibSignalStores';
|
||||||
|
import { typedArrayToArrayBuffer as toArrayBuffer } from '../Crypto';
|
||||||
import { updateConversationsWithUuidLookup } from '../updateConversationsWithUuidLookup';
|
import { updateConversationsWithUuidLookup } from '../updateConversationsWithUuidLookup';
|
||||||
import { getKeysForIdentifier } from './getKeysForIdentifier';
|
import { getKeysForIdentifier } from './getKeysForIdentifier';
|
||||||
|
|
||||||
|
@ -309,7 +310,7 @@ export default class OutgoingMessage {
|
||||||
this.plaintext = message.serialize();
|
this.plaintext = message.serialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.plaintext;
|
return toArrayBuffer(this.plaintext);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCiphertextMessage({
|
async getCiphertextMessage({
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
SenderKeyDistributionMessage,
|
SenderKeyDistributionMessage,
|
||||||
} from '@signalapp/signal-client';
|
} from '@signalapp/signal-client';
|
||||||
|
|
||||||
|
import { assert } from '../util/assert';
|
||||||
import { parseIntOrThrow } from '../util/parseIntOrThrow';
|
import { parseIntOrThrow } from '../util/parseIntOrThrow';
|
||||||
import { SenderKeys } from '../LibSignalStores';
|
import { SenderKeys } from '../LibSignalStores';
|
||||||
import {
|
import {
|
||||||
|
@ -750,8 +751,8 @@ export default class MessageSender {
|
||||||
|
|
||||||
const blockedIdentifiers = new Set(
|
const blockedIdentifiers = new Set(
|
||||||
concat(
|
concat(
|
||||||
window.storage.getBlockedUuids(),
|
window.storage.blocked.getBlockedUuids(),
|
||||||
window.storage.getBlockedNumbers()
|
window.storage.blocked.getBlockedNumbers()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -895,12 +896,13 @@ export default class MessageSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendIndividualProto(
|
async sendIndividualProto(
|
||||||
identifier: string,
|
identifier: string | undefined,
|
||||||
proto: DataMessageClass | ContentClass | PlaintextContent,
|
proto: DataMessageClass | ContentClass | PlaintextContent,
|
||||||
timestamp: number,
|
timestamp: number,
|
||||||
contentHint: number,
|
contentHint: number,
|
||||||
options?: SendOptionsType
|
options?: SendOptionsType
|
||||||
): Promise<CallbackResultType> {
|
): Promise<CallbackResultType> {
|
||||||
|
assert(identifier, "Identifier can't be undefined");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const callback = (res: CallbackResultType) => {
|
const callback = (res: CallbackResultType) => {
|
||||||
if (res && res.errors && res.errors.length > 0) {
|
if (res && res.errors && res.errors.length > 0) {
|
||||||
|
@ -976,7 +978,7 @@ export default class MessageSender {
|
||||||
const myUuid = window.textsecure.storage.user.getUuid();
|
const myUuid = window.textsecure.storage.user.getUuid();
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
|
|
||||||
if (myDevice === 1 || myDevice === '1') {
|
if (myDevice === 1) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1050,7 +1052,7 @@ export default class MessageSender {
|
||||||
const myNumber = window.textsecure.storage.user.getNumber();
|
const myNumber = window.textsecure.storage.user.getNumber();
|
||||||
const myUuid = window.textsecure.storage.user.getUuid();
|
const myUuid = window.textsecure.storage.user.getUuid();
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
if (myDevice !== 1 && myDevice !== '1') {
|
if (myDevice !== 1) {
|
||||||
const request = new window.textsecure.protobuf.SyncMessage.Request();
|
const request = new window.textsecure.protobuf.SyncMessage.Request();
|
||||||
request.type =
|
request.type =
|
||||||
window.textsecure.protobuf.SyncMessage.Request.Type.BLOCKED;
|
window.textsecure.protobuf.SyncMessage.Request.Type.BLOCKED;
|
||||||
|
@ -1081,7 +1083,7 @@ export default class MessageSender {
|
||||||
const myNumber = window.textsecure.storage.user.getNumber();
|
const myNumber = window.textsecure.storage.user.getNumber();
|
||||||
const myUuid = window.textsecure.storage.user.getUuid();
|
const myUuid = window.textsecure.storage.user.getUuid();
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
if (myDevice !== 1 && myDevice !== '1') {
|
if (myDevice !== 1) {
|
||||||
const request = new window.textsecure.protobuf.SyncMessage.Request();
|
const request = new window.textsecure.protobuf.SyncMessage.Request();
|
||||||
request.type =
|
request.type =
|
||||||
window.textsecure.protobuf.SyncMessage.Request.Type.CONFIGURATION;
|
window.textsecure.protobuf.SyncMessage.Request.Type.CONFIGURATION;
|
||||||
|
@ -1112,7 +1114,7 @@ export default class MessageSender {
|
||||||
const myNumber = window.textsecure.storage.user.getNumber();
|
const myNumber = window.textsecure.storage.user.getNumber();
|
||||||
const myUuid = window.textsecure.storage.user.getUuid();
|
const myUuid = window.textsecure.storage.user.getUuid();
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
if (myDevice !== 1 && myDevice !== '1') {
|
if (myDevice !== 1) {
|
||||||
const request = new window.textsecure.protobuf.SyncMessage.Request();
|
const request = new window.textsecure.protobuf.SyncMessage.Request();
|
||||||
request.type = window.textsecure.protobuf.SyncMessage.Request.Type.GROUPS;
|
request.type = window.textsecure.protobuf.SyncMessage.Request.Type.GROUPS;
|
||||||
const syncMessage = this.createSyncMessage();
|
const syncMessage = this.createSyncMessage();
|
||||||
|
@ -1143,7 +1145,7 @@ export default class MessageSender {
|
||||||
const myUuid = window.textsecure.storage.user.getUuid();
|
const myUuid = window.textsecure.storage.user.getUuid();
|
||||||
|
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
if (myDevice !== 1 && myDevice !== '1') {
|
if (myDevice !== 1) {
|
||||||
const request = new window.textsecure.protobuf.SyncMessage.Request();
|
const request = new window.textsecure.protobuf.SyncMessage.Request();
|
||||||
request.type =
|
request.type =
|
||||||
window.textsecure.protobuf.SyncMessage.Request.Type.CONTACTS;
|
window.textsecure.protobuf.SyncMessage.Request.Type.CONTACTS;
|
||||||
|
@ -1175,7 +1177,7 @@ export default class MessageSender {
|
||||||
const myNumber = window.textsecure.storage.user.getNumber();
|
const myNumber = window.textsecure.storage.user.getNumber();
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
|
|
||||||
if (myDevice === 1 || myDevice === '1') {
|
if (myDevice === 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1208,7 +1210,7 @@ export default class MessageSender {
|
||||||
const myNumber = window.textsecure.storage.user.getNumber();
|
const myNumber = window.textsecure.storage.user.getNumber();
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
|
|
||||||
if (myDevice === 1 || myDevice === '1') {
|
if (myDevice === 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1244,7 +1246,7 @@ export default class MessageSender {
|
||||||
const myNumber = window.textsecure.storage.user.getNumber();
|
const myNumber = window.textsecure.storage.user.getNumber();
|
||||||
const myUuid = window.textsecure.storage.user.getUuid();
|
const myUuid = window.textsecure.storage.user.getUuid();
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
if (myDevice !== 1 && myDevice !== '1') {
|
if (myDevice !== 1) {
|
||||||
const syncMessage = this.createSyncMessage();
|
const syncMessage = this.createSyncMessage();
|
||||||
syncMessage.read = [];
|
syncMessage.read = [];
|
||||||
for (let i = 0; i < reads.length; i += 1) {
|
for (let i = 0; i < reads.length; i += 1) {
|
||||||
|
@ -1283,7 +1285,7 @@ export default class MessageSender {
|
||||||
const myNumber = window.textsecure.storage.user.getNumber();
|
const myNumber = window.textsecure.storage.user.getNumber();
|
||||||
const myUuid = window.textsecure.storage.user.getUuid();
|
const myUuid = window.textsecure.storage.user.getUuid();
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
if (myDevice === 1 || myDevice === '1') {
|
if (myDevice === 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1323,7 +1325,7 @@ export default class MessageSender {
|
||||||
const myNumber = window.textsecure.storage.user.getNumber();
|
const myNumber = window.textsecure.storage.user.getNumber();
|
||||||
const myUuid = window.textsecure.storage.user.getUuid();
|
const myUuid = window.textsecure.storage.user.getUuid();
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
if (myDevice === 1 || myDevice === '1') {
|
if (myDevice === 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1361,7 +1363,7 @@ export default class MessageSender {
|
||||||
options?: SendOptionsType
|
options?: SendOptionsType
|
||||||
): Promise<CallbackResultType | null> {
|
): Promise<CallbackResultType | null> {
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
if (myDevice === 1 || myDevice === '1') {
|
if (myDevice === 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1412,7 +1414,7 @@ export default class MessageSender {
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
if (myDevice === 1 || myDevice === '1') {
|
if (myDevice === 1) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1526,7 +1528,7 @@ export default class MessageSender {
|
||||||
const myDevice = window.textsecure.storage.user.getDeviceId();
|
const myDevice = window.textsecure.storage.user.getDeviceId();
|
||||||
if (
|
if (
|
||||||
(myNumber === recipientE164 || myUuid === recipientUuid) &&
|
(myNumber === recipientE164 || myUuid === recipientUuid) &&
|
||||||
(myDevice === 1 || myDevice === '1')
|
myDevice === 1
|
||||||
) {
|
) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +1,149 @@
|
||||||
// Copyright 2020-2021 Signal Messenger, LLC
|
// Copyright 2020-2021 Signal Messenger, LLC
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
/* eslint-disable no-restricted-syntax */
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
import {
|
||||||
import utils from './Helpers';
|
StorageAccessType as Access,
|
||||||
|
StorageInterface,
|
||||||
|
} from '../types/Storage.d';
|
||||||
|
import { User } from './storage/User';
|
||||||
|
import { Blocked } from './storage/Blocked';
|
||||||
|
|
||||||
// Default implementation working with localStorage
|
import { assert } from '../util/assert';
|
||||||
const localStorageImpl: StorageInterface = {
|
import Data from '../sql/Client';
|
||||||
put(key: string, value: any) {
|
import { SignalProtocolStore } from '../SignalProtocolStore';
|
||||||
if (value === undefined) {
|
|
||||||
throw new Error('Tried to store undefined');
|
export class Storage implements StorageInterface {
|
||||||
|
public readonly user: User;
|
||||||
|
|
||||||
|
public readonly blocked: Blocked;
|
||||||
|
|
||||||
|
private ready = false;
|
||||||
|
|
||||||
|
private readyCallbacks: Array<() => void> = [];
|
||||||
|
|
||||||
|
private items: Partial<Access> = Object.create(null);
|
||||||
|
|
||||||
|
private privProtocol: SignalProtocolStore | undefined;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.user = new User(this);
|
||||||
|
this.blocked = new Blocked(this);
|
||||||
|
|
||||||
|
window.storage = this;
|
||||||
}
|
}
|
||||||
localStorage.setItem(`${key}`, utils.jsonThing(value));
|
|
||||||
},
|
|
||||||
|
|
||||||
get(key: string, defaultValue: any) {
|
get protocol(): SignalProtocolStore {
|
||||||
const value = localStorage.getItem(`${key}`);
|
assert(
|
||||||
if (value === null) {
|
this.privProtocol !== undefined,
|
||||||
|
'SignalProtocolStore not initialized'
|
||||||
|
);
|
||||||
|
return this.privProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
set protocol(value: SignalProtocolStore) {
|
||||||
|
this.privProtocol = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// `StorageInterface` implementation
|
||||||
|
|
||||||
|
public get<K extends keyof Access, V extends Access[K]>(
|
||||||
|
key: K
|
||||||
|
): V | undefined;
|
||||||
|
|
||||||
|
public get<K extends keyof Access, V extends Access[K]>(
|
||||||
|
key: K,
|
||||||
|
defaultValue: V
|
||||||
|
): V;
|
||||||
|
|
||||||
|
public get<K extends keyof Access, V extends Access[K]>(
|
||||||
|
key: K,
|
||||||
|
defaultValue?: V
|
||||||
|
): V | undefined {
|
||||||
|
if (!this.ready) {
|
||||||
|
window.log.warn('Called storage.get before storage is ready. key:', key);
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = this.items[key];
|
||||||
|
if (item === undefined) {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
return JSON.parse(value);
|
|
||||||
},
|
|
||||||
|
|
||||||
remove(key: string) {
|
return item as V;
|
||||||
localStorage.removeItem(`${key}`);
|
}
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export type StorageInterface = {
|
public async put<K extends keyof Access>(
|
||||||
put(key: string, value: any): void | Promise<void>;
|
key: K,
|
||||||
get(key: string, defaultValue: any): any;
|
value: Access[K]
|
||||||
remove(key: string): void | Promise<void>;
|
): Promise<void> {
|
||||||
};
|
if (!this.ready) {
|
||||||
|
window.log.warn('Called storage.put before storage is ready. key:', key);
|
||||||
|
}
|
||||||
|
|
||||||
const Storage = {
|
this.items[key] = value;
|
||||||
impl: localStorageImpl,
|
await window.Signal.Data.createOrUpdateItem({ id: key, value });
|
||||||
|
|
||||||
put(key: string, value: unknown): Promise<void> | void {
|
window.reduxActions?.items.putItemExternal(key, value);
|
||||||
return Storage.impl.put(key, value);
|
}
|
||||||
},
|
|
||||||
|
|
||||||
get(key: string, defaultValue: unknown): Promise<unknown> {
|
public async remove<K extends keyof Access>(key: K): Promise<void> {
|
||||||
return Storage.impl.get(key, defaultValue);
|
if (!this.ready) {
|
||||||
},
|
window.log.warn(
|
||||||
|
'Called storage.remove before storage is ready. key:',
|
||||||
|
key
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
remove(key: string): Promise<void> | void {
|
delete this.items[key];
|
||||||
return Storage.impl.remove(key);
|
await Data.removeItemById(key);
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Storage;
|
window.reduxActions?.items.removeItemExternal(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regular methods
|
||||||
|
|
||||||
|
public onready(callback: () => void): void {
|
||||||
|
if (this.ready) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
this.readyCallbacks.push(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async fetch(): Promise<void> {
|
||||||
|
this.reset();
|
||||||
|
|
||||||
|
Object.assign(this.items, await Data.getAllItems());
|
||||||
|
|
||||||
|
this.ready = true;
|
||||||
|
this.callListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
public reset(): void {
|
||||||
|
this.ready = false;
|
||||||
|
this.items = Object.create(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getItemsState(): Partial<Access> {
|
||||||
|
const state = Object.create(null);
|
||||||
|
|
||||||
|
// TypeScript isn't smart enough to figure out the types automatically.
|
||||||
|
const { items } = this;
|
||||||
|
const allKeys = Object.keys(items) as Array<keyof typeof items>;
|
||||||
|
|
||||||
|
for (const key of allKeys) {
|
||||||
|
state[key] = items[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private callListeners(): void {
|
||||||
|
if (!this.ready) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const callbacks = this.readyCallbacks;
|
||||||
|
this.readyCallbacks = [];
|
||||||
|
callbacks.forEach(callback => callback());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
12
ts/textsecure/Types.d.ts
vendored
12
ts/textsecure/Types.d.ts
vendored
|
@ -11,6 +11,16 @@ export {
|
||||||
UnprocessedUpdateType,
|
UnprocessedUpdateType,
|
||||||
} from '../sql/Interface';
|
} from '../sql/Interface';
|
||||||
|
|
||||||
|
export type StorageServiceCallOptionsType = {
|
||||||
|
credentials?: StorageServiceCredentials;
|
||||||
|
greaterThanVersion?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StorageServiceCredentials = {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type DeviceType = {
|
export type DeviceType = {
|
||||||
id: number;
|
id: number;
|
||||||
identifier: string;
|
identifier: string;
|
||||||
|
@ -44,3 +54,5 @@ export type OuterSignedPrekeyType = {
|
||||||
privKey: ArrayBuffer;
|
privKey: ArrayBuffer;
|
||||||
pubKey: ArrayBuffer;
|
pubKey: ArrayBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SessionResetsType = Record<string, number>;
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
import { connection as WebSocket, IMessage } from 'websocket';
|
import { connection as WebSocket, IMessage } from 'websocket';
|
||||||
|
|
||||||
import { ByteBufferClass } from '../window.d';
|
import { ByteBufferClass } from '../window.d';
|
||||||
|
import { typedArrayToArrayBuffer as toArrayBuffer } from '../Crypto';
|
||||||
|
|
||||||
import EventTarget from './EventTarget';
|
import EventTarget from './EventTarget';
|
||||||
|
|
||||||
|
@ -153,7 +154,7 @@ export default class WebSocketResource extends EventTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
const message = window.textsecure.protobuf.WebSocketMessage.decode(
|
||||||
binaryData
|
toArrayBuffer(binaryData)
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
message.type ===
|
message.type ===
|
||||||
|
|
|
@ -11,7 +11,7 @@ import createTaskWithTimeout from './TaskWithTimeout';
|
||||||
import SyncRequest from './SyncRequest';
|
import SyncRequest from './SyncRequest';
|
||||||
import MessageSender from './SendMessage';
|
import MessageSender from './SendMessage';
|
||||||
import StringView from './StringView';
|
import StringView from './StringView';
|
||||||
import Storage from './Storage';
|
import { Storage } from './Storage';
|
||||||
import * as WebAPI from './WebAPI';
|
import * as WebAPI from './WebAPI';
|
||||||
import WebSocketResource from './WebsocketResources';
|
import WebSocketResource from './WebsocketResources';
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ export const textsecure = {
|
||||||
createTaskWithTimeout,
|
createTaskWithTimeout,
|
||||||
crypto: Crypto,
|
crypto: Crypto,
|
||||||
utils,
|
utils,
|
||||||
storage: Storage,
|
storage: new Storage(),
|
||||||
|
|
||||||
AccountManager,
|
AccountManager,
|
||||||
ContactBuffer,
|
ContactBuffer,
|
||||||
|
|
98
ts/textsecure/storage/Blocked.ts
Normal file
98
ts/textsecure/storage/Blocked.ts
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// Copyright 2016-2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { without } from 'lodash';
|
||||||
|
|
||||||
|
import { StorageInterface } from '../../types/Storage.d';
|
||||||
|
|
||||||
|
const BLOCKED_NUMBERS_ID = 'blocked';
|
||||||
|
const BLOCKED_UUIDS_ID = 'blocked-uuids';
|
||||||
|
const BLOCKED_GROUPS_ID = 'blocked-groups';
|
||||||
|
|
||||||
|
export class Blocked {
|
||||||
|
constructor(private readonly storage: StorageInterface) {}
|
||||||
|
|
||||||
|
public getBlockedNumbers(): Array<string> {
|
||||||
|
return this.storage.get(BLOCKED_NUMBERS_ID, new Array<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public isBlocked(number: string): boolean {
|
||||||
|
return this.getBlockedNumbers().includes(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async addBlockedNumber(number: string): Promise<void> {
|
||||||
|
const numbers = this.getBlockedNumbers();
|
||||||
|
if (numbers.includes(number)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.log.info('adding', number, 'to blocked list');
|
||||||
|
await this.storage.put(BLOCKED_NUMBERS_ID, numbers.concat(number));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async removeBlockedNumber(number: string): Promise<void> {
|
||||||
|
const numbers = this.getBlockedNumbers();
|
||||||
|
if (!numbers.includes(number)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.log.info('removing', number, 'from blocked list');
|
||||||
|
await this.storage.put(BLOCKED_NUMBERS_ID, without(numbers, number));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getBlockedUuids(): Array<string> {
|
||||||
|
return this.storage.get(BLOCKED_UUIDS_ID, new Array<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public isUuidBlocked(uuid: string): boolean {
|
||||||
|
return this.getBlockedUuids().includes(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async addBlockedUuid(uuid: string): Promise<void> {
|
||||||
|
const uuids = this.getBlockedUuids();
|
||||||
|
if (uuids.includes(uuid)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.log.info('adding', uuid, 'to blocked list');
|
||||||
|
await this.storage.put(BLOCKED_UUIDS_ID, uuids.concat(uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async removeBlockedUuid(uuid: string): Promise<void> {
|
||||||
|
const numbers = this.getBlockedUuids();
|
||||||
|
if (!numbers.includes(uuid)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.log.info('removing', uuid, 'from blocked list');
|
||||||
|
await this.storage.put(BLOCKED_UUIDS_ID, without(numbers, uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
public getBlockedGroups(): Array<string> {
|
||||||
|
return this.storage.get(BLOCKED_GROUPS_ID, new Array<string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public isGroupBlocked(groupId: string): boolean {
|
||||||
|
return this.getBlockedGroups().includes(groupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async addBlockedGroup(groupId: string): Promise<void> {
|
||||||
|
const groupIds = this.getBlockedGroups();
|
||||||
|
if (groupIds.includes(groupId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.log.info(`adding group(${groupId}) to blocked list`);
|
||||||
|
await this.storage.put(BLOCKED_GROUPS_ID, groupIds.concat(groupId));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async removeBlockedGroup(groupId: string): Promise<void> {
|
||||||
|
const groupIds = this.getBlockedGroups();
|
||||||
|
if (!groupIds.includes(groupId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.log.info(`removing group(${groupId} from blocked list`);
|
||||||
|
await this.storage.put(BLOCKED_GROUPS_ID, without(groupIds, groupId));
|
||||||
|
}
|
||||||
|
}
|
76
ts/textsecure/storage/User.ts
Normal file
76
ts/textsecure/storage/User.ts
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
// Copyright 2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import { StorageInterface } from '../../types/Storage.d';
|
||||||
|
|
||||||
|
import Helpers from '../Helpers';
|
||||||
|
|
||||||
|
export class User {
|
||||||
|
constructor(private readonly storage: StorageInterface) {}
|
||||||
|
|
||||||
|
public async setNumberAndDeviceId(
|
||||||
|
number: string,
|
||||||
|
deviceId: number,
|
||||||
|
deviceName?: string
|
||||||
|
): Promise<void> {
|
||||||
|
await this.storage.put('number_id', `${number}.${deviceId}`);
|
||||||
|
if (deviceName) {
|
||||||
|
await this.storage.put('device_name', deviceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setUuidAndDeviceId(
|
||||||
|
uuid: string,
|
||||||
|
deviceId: number
|
||||||
|
): Promise<void> {
|
||||||
|
return this.storage.put('uuid_id', `${uuid}.${deviceId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNumber(): string | undefined {
|
||||||
|
const numberId = this.storage.get('number_id');
|
||||||
|
if (numberId === undefined) return undefined;
|
||||||
|
return Helpers.unencodeNumber(numberId)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getUuid(): string | undefined {
|
||||||
|
const uuid = this.storage.get('uuid_id');
|
||||||
|
if (uuid === undefined) return undefined;
|
||||||
|
return Helpers.unencodeNumber(uuid.toLowerCase())[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDeviceId(): number | undefined {
|
||||||
|
const value = this._getDeviceIdFromUuid() || this._getDeviceIdFromNumber();
|
||||||
|
if (value === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return parseInt(value, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDeviceName(): string | undefined {
|
||||||
|
return this.storage.get('device_name');
|
||||||
|
}
|
||||||
|
|
||||||
|
public async setDeviceNameEncrypted(): Promise<void> {
|
||||||
|
return this.storage.put('deviceNameEncrypted', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getDeviceNameEncrypted(): boolean | undefined {
|
||||||
|
return this.storage.get('deviceNameEncrypted');
|
||||||
|
}
|
||||||
|
|
||||||
|
public getSignalingKey(): ArrayBuffer | undefined {
|
||||||
|
return this.storage.get('signaling_key');
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getDeviceIdFromUuid(): string | undefined {
|
||||||
|
const uuid = this.storage.get('uuid_id');
|
||||||
|
if (uuid === undefined) return undefined;
|
||||||
|
return Helpers.unencodeNumber(uuid)[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getDeviceIdFromNumber(): string | undefined {
|
||||||
|
const numberId = this.storage.get('number_id');
|
||||||
|
if (numberId === undefined) return undefined;
|
||||||
|
return Helpers.unencodeNumber(numberId)[1];
|
||||||
|
}
|
||||||
|
}
|
|
@ -105,3 +105,8 @@ export type DefaultConversationColorType = {
|
||||||
export const DEFAULT_CONVERSATION_COLOR: DefaultConversationColorType = {
|
export const DEFAULT_CONVERSATION_COLOR: DefaultConversationColorType = {
|
||||||
color: 'ultramarine',
|
color: 'ultramarine',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type CustomColorsItemType = {
|
||||||
|
readonly colors: Record<string, CustomColorType>;
|
||||||
|
readonly version: number;
|
||||||
|
};
|
||||||
|
|
133
ts/types/Storage.d.ts
vendored
Normal file
133
ts/types/Storage.d.ts
vendored
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
// Copyright 2020-2021 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import type {
|
||||||
|
CustomColorsItemType,
|
||||||
|
DefaultConversationColorType,
|
||||||
|
} from './Colors';
|
||||||
|
import type { AudioDevice } from './Calling';
|
||||||
|
import type { PhoneNumberDiscoverability } from '../util/phoneNumberDiscoverability';
|
||||||
|
import type { PhoneNumberSharingMode } from '../util/phoneNumberSharingMode';
|
||||||
|
import type { RetryItemType } from '../util/retryPlaceholders';
|
||||||
|
import type { ConfigMapType as RemoteConfigType } from '../RemoteConfig';
|
||||||
|
|
||||||
|
import type { GroupCredentialType } from '../textsecure/WebAPI';
|
||||||
|
import type {
|
||||||
|
KeyPairType,
|
||||||
|
SessionResetsType,
|
||||||
|
StorageServiceCredentials,
|
||||||
|
} from '../textsecure/Types.d';
|
||||||
|
|
||||||
|
export type SerializedCertificateType = {
|
||||||
|
expires: number;
|
||||||
|
serialized: ArrayBuffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StorageAccessType = {
|
||||||
|
'always-relay-calls': boolean;
|
||||||
|
'audio-notification': boolean;
|
||||||
|
'badge-count-muted-conversations': boolean;
|
||||||
|
'blocked-groups': Array<string>;
|
||||||
|
'blocked-uuids': Array<string>;
|
||||||
|
'call-ringtone-notification': boolean;
|
||||||
|
'call-system-notification': boolean;
|
||||||
|
'hide-menu-bar': boolean;
|
||||||
|
'incoming-call-notification': boolean;
|
||||||
|
'notification-draw-attention': boolean;
|
||||||
|
'notification-setting': 'message' | 'name' | 'count' | 'off';
|
||||||
|
'read-receipt-setting': boolean;
|
||||||
|
'spell-check': boolean;
|
||||||
|
'theme-setting': 'light' | 'dark' | 'system';
|
||||||
|
attachmentMigration_isComplete: boolean;
|
||||||
|
attachmentMigration_lastProcessedIndex: number;
|
||||||
|
blocked: Array<string>;
|
||||||
|
defaultConversationColor: DefaultConversationColorType;
|
||||||
|
customColors: CustomColorsItemType;
|
||||||
|
device_name: string;
|
||||||
|
hasRegisterSupportForUnauthenticatedDelivery: boolean;
|
||||||
|
identityKey: KeyPairType;
|
||||||
|
lastHeartbeat: number;
|
||||||
|
lastStartup: number;
|
||||||
|
lastAttemptedToRefreshProfilesAt: number;
|
||||||
|
maxPreKeyId: number;
|
||||||
|
number_id: string;
|
||||||
|
password: string;
|
||||||
|
profileKey: ArrayBuffer;
|
||||||
|
regionCode: string;
|
||||||
|
registrationId: number;
|
||||||
|
remoteBuildExpiration: number;
|
||||||
|
sessionResets: SessionResetsType;
|
||||||
|
showStickerPickerHint: boolean;
|
||||||
|
showStickersIntroduction: boolean;
|
||||||
|
signedKeyId: number;
|
||||||
|
signedKeyRotationRejected: number;
|
||||||
|
storageKey: string;
|
||||||
|
synced_at: number;
|
||||||
|
userAgent: string;
|
||||||
|
uuid_id: string;
|
||||||
|
version: string;
|
||||||
|
linkPreviews: boolean;
|
||||||
|
universalExpireTimer: number;
|
||||||
|
retryPlaceholders: Array<RetryItemType>;
|
||||||
|
chromiumRegistrationDoneEver: '';
|
||||||
|
chromiumRegistrationDone: '';
|
||||||
|
phoneNumberSharingMode: PhoneNumberSharingMode;
|
||||||
|
phoneNumberDiscoverability: PhoneNumberDiscoverability;
|
||||||
|
pinnedConversationIds: Array<string>;
|
||||||
|
primarySendsSms: boolean;
|
||||||
|
typingIndicators: boolean;
|
||||||
|
sealedSenderIndicators: boolean;
|
||||||
|
storageFetchComplete: boolean;
|
||||||
|
avatarUrl: string;
|
||||||
|
manifestVersion: number;
|
||||||
|
storageCredentials: StorageServiceCredentials;
|
||||||
|
'storage-service-error-records': Array<{
|
||||||
|
itemType: number;
|
||||||
|
storageID: string;
|
||||||
|
}>;
|
||||||
|
'storage-service-unknown-records': Array<{
|
||||||
|
itemType: number;
|
||||||
|
storageID: string;
|
||||||
|
}>;
|
||||||
|
'preferred-video-input-device': string;
|
||||||
|
'preferred-audio-input-device': AudioDevice;
|
||||||
|
'preferred-audio-output-device': AudioDevice;
|
||||||
|
remoteConfig: RemoteConfigType;
|
||||||
|
unidentifiedDeliveryIndicators: boolean;
|
||||||
|
groupCredentials: Array<GroupCredentialType>;
|
||||||
|
lastReceivedAtCounter: number;
|
||||||
|
signaling_key: ArrayBuffer;
|
||||||
|
skinTone: number;
|
||||||
|
unreadCount: number;
|
||||||
|
'challenge:retry-message-ids': ReadonlyArray<{
|
||||||
|
messageId: string;
|
||||||
|
createdAt: number;
|
||||||
|
}>;
|
||||||
|
deviceNameEncrypted: boolean;
|
||||||
|
'indexeddb-delete-needed': boolean;
|
||||||
|
senderCertificate: SerializedCertificateType;
|
||||||
|
senderCertificateNoE164: SerializedCertificateType;
|
||||||
|
|
||||||
|
// Deprecated
|
||||||
|
senderCertificateWithUuid: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface StorageInterface {
|
||||||
|
onready(callback: () => void): void;
|
||||||
|
|
||||||
|
get<K extends keyof StorageAccessType, V extends StorageAccessType[K]>(
|
||||||
|
key: K
|
||||||
|
): V | undefined;
|
||||||
|
|
||||||
|
get<K extends keyof StorageAccessType, V extends StorageAccessType[K]>(
|
||||||
|
key: K,
|
||||||
|
defaultValue: V
|
||||||
|
): V;
|
||||||
|
|
||||||
|
put<K extends keyof StorageAccessType>(
|
||||||
|
key: K,
|
||||||
|
value: StorageAccessType[K]
|
||||||
|
): Promise<void>;
|
||||||
|
|
||||||
|
remove<K extends keyof StorageAccessType>(key: K): Promise<void>;
|
||||||
|
}
|
|
@ -2,15 +2,11 @@
|
||||||
// SPDX-License-Identifier: AGPL-3.0-only
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
import type { WebAPIConnectType, WebAPIType } from '../textsecure/WebAPI';
|
import type { WebAPIConnectType, WebAPIType } from '../textsecure/WebAPI';
|
||||||
|
import { StorageInterface } from '../types/Storage.d';
|
||||||
// We define a stricter storage here that returns `unknown` instead of `any`.
|
|
||||||
type Storage = {
|
|
||||||
get(key: string): unknown;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function connectToServerWithStoredCredentials(
|
export function connectToServerWithStoredCredentials(
|
||||||
WebAPI: WebAPIConnectType,
|
WebAPI: WebAPIConnectType,
|
||||||
storage: Storage
|
storage: Pick<StorageInterface, 'get'>
|
||||||
): WebAPIType {
|
): WebAPIType {
|
||||||
const username = storage.get('uuid_id') || storage.get('number_id');
|
const username = storage.get('uuid_id') || storage.get('number_id');
|
||||||
if (typeof username !== 'string') {
|
if (typeof username !== 'string') {
|
||||||
|
|
|
@ -52,7 +52,7 @@ export class RetryPlaceholders {
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsed = retryItemListSchema.safeParse(
|
const parsed = retryItemListSchema.safeParse(
|
||||||
window.storage.get(STORAGE_KEY) || []
|
window.storage.get(STORAGE_KEY, new Array<RetryItemType>())
|
||||||
);
|
);
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
window.log.warn(
|
window.log.warn(
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
import { PublicKey, Fingerprint } from '@signalapp/signal-client';
|
import { PublicKey, Fingerprint } from '@signalapp/signal-client';
|
||||||
import { ConversationType } from '../state/ducks/conversations';
|
import { ConversationType } from '../state/ducks/conversations';
|
||||||
|
|
||||||
|
import { assert } from './assert';
|
||||||
|
|
||||||
export async function generateSecurityNumber(
|
export async function generateSecurityNumber(
|
||||||
ourNumber: string,
|
ourNumber: string,
|
||||||
ourKey: ArrayBuffer,
|
ourKey: ArrayBuffer,
|
||||||
|
@ -35,7 +37,7 @@ export async function generateSecurityNumberBlock(
|
||||||
const ourUuid = window.textsecure.storage.user.getUuid();
|
const ourUuid = window.textsecure.storage.user.getUuid();
|
||||||
|
|
||||||
const us = window.textsecure.storage.protocol.getIdentityRecord(
|
const us = window.textsecure.storage.protocol.getIdentityRecord(
|
||||||
ourUuid || ourNumber
|
ourUuid || ourNumber || ''
|
||||||
);
|
);
|
||||||
const ourKey = us ? us.publicKey : null;
|
const ourKey = us ? us.publicKey : null;
|
||||||
|
|
||||||
|
@ -57,6 +59,7 @@ export async function generateSecurityNumberBlock(
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(ourNumber, 'Should have our number');
|
||||||
const securityNumber = await generateSecurityNumber(
|
const securityNumber = await generateSecurityNumber(
|
||||||
ourNumber,
|
ourNumber,
|
||||||
ourKey,
|
ourKey,
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
SenderCertificate,
|
SenderCertificate,
|
||||||
UnidentifiedSenderMessageContent,
|
UnidentifiedSenderMessageContent,
|
||||||
} from '@signalapp/signal-client';
|
} from '@signalapp/signal-client';
|
||||||
|
import { typedArrayToArrayBuffer as toArrayBuffer } from '../Crypto';
|
||||||
import { senderCertificateService } from '../services/senderCertificate';
|
import { senderCertificateService } from '../services/senderCertificate';
|
||||||
import {
|
import {
|
||||||
padMessage,
|
padMessage,
|
||||||
|
@ -371,8 +372,8 @@ export async function sendToGroupViaSenderKey(options: {
|
||||||
const accessKeys = getXorOfAccessKeys(devicesForSenderKey);
|
const accessKeys = getXorOfAccessKeys(devicesForSenderKey);
|
||||||
|
|
||||||
const result = await window.textsecure.messaging.sendWithSenderKey(
|
const result = await window.textsecure.messaging.sendWithSenderKey(
|
||||||
messageBuffer,
|
toArrayBuffer(messageBuffer),
|
||||||
accessKeys,
|
toArrayBuffer(accessKeys),
|
||||||
timestamp,
|
timestamp,
|
||||||
online
|
online
|
||||||
);
|
);
|
||||||
|
|
|
@ -460,9 +460,9 @@ Whisper.ConversationView = Whisper.View.extend({
|
||||||
const { model }: { model: ConversationModel } = this;
|
const { model }: { model: ConversationModel } = this;
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
const pinnedConversationIds = window.storage.get<Array<string>>(
|
const pinnedConversationIds = window.storage.get(
|
||||||
'pinnedConversationIds',
|
'pinnedConversationIds',
|
||||||
[]
|
new Array<string>()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (pinnedConversationIds.length >= 4) {
|
if (pinnedConversationIds.length >= 4) {
|
||||||
|
@ -3880,14 +3880,14 @@ Whisper.ConversationView = Whisper.View.extend({
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
isDirectConversation(this.model.attributes) &&
|
isDirectConversation(this.model.attributes) &&
|
||||||
(window.storage.isBlocked(this.model.get('e164')) ||
|
(window.storage.blocked.isBlocked(this.model.get('e164')) ||
|
||||||
window.storage.isUuidBlocked(this.model.get('uuid')))
|
window.storage.blocked.isUuidBlocked(this.model.get('uuid')))
|
||||||
) {
|
) {
|
||||||
ToastView = Whisper.BlockedToast;
|
ToastView = Whisper.BlockedToast;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!isDirectConversation(this.model.attributes) &&
|
!isDirectConversation(this.model.attributes) &&
|
||||||
window.storage.isGroupBlocked(this.model.get('groupId'))
|
window.storage.blocked.isGroupBlocked(this.model.get('groupId'))
|
||||||
) {
|
) {
|
||||||
ToastView = Whisper.BlockedGroupToast;
|
ToastView = Whisper.BlockedGroupToast;
|
||||||
}
|
}
|
||||||
|
|
40
ts/window.d.ts
vendored
40
ts/window.d.ts
vendored
|
@ -20,6 +20,7 @@ import {
|
||||||
ReactionModelType,
|
ReactionModelType,
|
||||||
} from './model-types.d';
|
} from './model-types.d';
|
||||||
import { ContactRecordIdentityState, TextSecureType } from './textsecure.d';
|
import { ContactRecordIdentityState, TextSecureType } from './textsecure.d';
|
||||||
|
import { Storage } from './textsecure/Storage';
|
||||||
import {
|
import {
|
||||||
ChallengeHandler,
|
ChallengeHandler,
|
||||||
IPCRequest as IPCChallengeRequest,
|
IPCRequest as IPCChallengeRequest,
|
||||||
|
@ -248,30 +249,7 @@ declare global {
|
||||||
setMenuBarVisibility: (value: WhatIsThis) => void;
|
setMenuBarVisibility: (value: WhatIsThis) => void;
|
||||||
showConfirmationDialog: (options: ConfirmationDialogViewProps) => void;
|
showConfirmationDialog: (options: ConfirmationDialogViewProps) => void;
|
||||||
showKeyboardShortcuts: () => void;
|
showKeyboardShortcuts: () => void;
|
||||||
storage: {
|
storage: Storage;
|
||||||
addBlockedGroup: (group: string) => void;
|
|
||||||
addBlockedNumber: (number: string) => void;
|
|
||||||
addBlockedUuid: (uuid: string) => void;
|
|
||||||
fetch: () => void;
|
|
||||||
get: {
|
|
||||||
<T = any>(key: string): T | undefined;
|
|
||||||
<T>(key: string, defaultValue: T): T;
|
|
||||||
};
|
|
||||||
getBlockedGroups: () => Array<string>;
|
|
||||||
getBlockedNumbers: () => Array<string>;
|
|
||||||
getBlockedUuids: () => Array<string>;
|
|
||||||
getItemsState: () => WhatIsThis;
|
|
||||||
isBlocked: (number: string) => boolean;
|
|
||||||
isGroupBlocked: (group: unknown) => boolean;
|
|
||||||
isUuidBlocked: (uuid: string) => boolean;
|
|
||||||
onready: (callback: () => unknown) => void;
|
|
||||||
put: (key: string, value: any) => Promise<void>;
|
|
||||||
remove: (key: string) => Promise<void>;
|
|
||||||
removeBlockedGroup: (group: string) => void;
|
|
||||||
removeBlockedNumber: (number: string) => void;
|
|
||||||
removeBlockedUuid: (uuid: string) => void;
|
|
||||||
reset: () => void;
|
|
||||||
};
|
|
||||||
systemTheme: WhatIsThis;
|
systemTheme: WhatIsThis;
|
||||||
textsecure: TextSecureType;
|
textsecure: TextSecureType;
|
||||||
synchronousCrypto: typeof synchronousCrypto;
|
synchronousCrypto: typeof synchronousCrypto;
|
||||||
|
@ -595,6 +573,20 @@ declare global {
|
||||||
interface Error {
|
interface Error {
|
||||||
originalError?: Event;
|
originalError?: Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Uint8Array and ArrayBuffer are type-compatible in TypeScript's covariant
|
||||||
|
// type checker, but in reality they are not. Let's assert correct use!
|
||||||
|
interface Uint8Array {
|
||||||
|
__uint8array: never;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ArrayBuffer {
|
||||||
|
__array_buffer: never;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SharedArrayBuffer {
|
||||||
|
__array_buffer: never;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DCodeIOType = {
|
export type DCodeIOType = {
|
||||||
|
|
Loading…
Add table
Reference in a new issue