From 9acb189650ccd1494323a284cefc149ac35db880 Mon Sep 17 00:00:00 2001 From: Scott Nonnenberg Date: Fri, 23 Feb 2018 17:40:02 -0800 Subject: [PATCH] Remove config after unlink, clear db when linked with new number --- js/background.js | 19 +++++++++++++++- js/backup.js | 17 ++++++++++---- js/conversation_controller.js | 2 +- js/registration.js | 5 +++- js/signal_protocol_store.js | 39 ++++++++++++++++++++++++++++---- js/storage.js | 4 ++++ libtextsecure/account_manager.js | 33 +++++++++++++++++++++++++++ 7 files changed, 108 insertions(+), 11 deletions(-) diff --git a/js/background.js b/js/background.js index f78836b36..e4ad04066 100644 --- a/js/background.js +++ b/js/background.js @@ -694,8 +694,25 @@ console.log('background onError:', Errors.toLogFormat(error)); if (error.name === 'HTTPError' && (error.code == 401 || error.code == 403)) { - Whisper.Registration.remove(); Whisper.events.trigger('unauthorized'); + + console.log('Client is no longer authorized; deleting local configuration'); + Whisper.Registration.remove(); + var previousNumberId = textsecure.storage.get('number_id'); + + textsecure.storage.protocol.removeAllConfiguration().then(function() { + // 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. + Whisper.Registration.markEverDone(); + textsecure.storage.put('number_id', previousNumberId); + console.log('Successfully cleared local configuration'); + }, function(error) { + console.log( + 'Something went wrong clearing local configuration', + error && error.stack ? error.stack : error + ); + }); + return; } diff --git a/js/backup.js b/js/backup.js index 2ccf8ed3a..3e0717199 100644 --- a/js/backup.js +++ b/js/backup.js @@ -913,9 +913,13 @@ } function clearAllStores(idb_db) { + return clearStores(idb_db); + } + + function clearStores(idb_db, names) { return new Promise(function(resolve, reject) { - console.log('Clearing all indexeddb stores'); - var storeNames = idb_db.objectStoreNames; + var storeNames = names || idb_db.objectStoreNames; + console.log('Clearing these indexeddb stores:', storeNames); var transaction = idb_db.transaction(storeNames, 'readwrite'); var finished = false; @@ -930,7 +934,7 @@ transaction.oncomplete = finish.bind(null, 'transaction complete'); transaction.onerror = function(e) { handleDOMException( - 'clearAllStores transaction error', + 'clearStores transaction error', transaction.error, reject ); @@ -953,7 +957,7 @@ request.onerror = function(e) { handleDOMException( - 'clearAllStores request error', + 'clearStores request error', request.error, reject ); @@ -973,6 +977,11 @@ return clearAllStores(idb_db); }); }, + clearStores: function(storeNames) { + return openDatabase().then(function(idb_db) { + return clearStores(idb_db, storeNames); + }); + }, getDirectoryForExport: function() { var options = { title: i18n('exportChooserTitle'), diff --git a/js/conversation_controller.js b/js/conversation_controller.js index 043f5aa5a..cb47fc162 100644 --- a/js/conversation_controller.js +++ b/js/conversation_controller.js @@ -161,7 +161,7 @@ return this._initialPromise; }, reset: function() { - this._initialPromise = null; + this._initialPromise = Promise.resolve(); conversations.reset([]); }, load: function() { diff --git a/js/registration.js b/js/registration.js index 8e3aadb9a..e482f2cb8 100644 --- a/js/registration.js +++ b/js/registration.js @@ -4,8 +4,11 @@ (function () { 'use strict'; Whisper.Registration = { - markDone: function () { + markEverDone: function() { storage.put('chromiumRegistrationDoneEver', ''); + }, + markDone: function () { + this.markEverDone(); storage.put('chromiumRegistrationDone', ''); }, isDone: function () { diff --git a/js/signal_protocol_store.js b/js/signal_protocol_store.js index 456fefa9b..e653ff170 100644 --- a/js/signal_protocol_store.js +++ b/js/signal_protocol_store.js @@ -171,18 +171,18 @@ constructor: SignalProtocolStore, getIdentityKeyPair: function() { var item = new Item({id: 'identityKey'}); - return new Promise(function(resolve) { + return new Promise(function(resolve, reject) { item.fetch().then(function() { resolve(item.get('value')); - }); + }, reject); }); }, getLocalRegistrationId: function() { var item = new Item({id: 'registrationId'}); - return new Promise(function(resolve) { + return new Promise(function(resolve, reject) { item.fetch().then(function() { resolve(item.get('value')); - }); + }, reject); }); }, @@ -854,6 +854,37 @@ return unprocessed.destroy().then(resolve, reject); }.bind(this)); }, + removeAllData: function() { + // First the in-memory caches: + window.storage.reset(); // items store + ConversationController.reset(); // conversations store + + // Then, the database. All stores: + // items + // identityKeys + // sessions + // signedPreKeys + // preKeys + // unprocessed + // groups + // conversations + // messages + return window.Whisper.Backup.clearDatabase(); + }, + removeAllConfiguration: function() { + // First the in-memory cache for the items store: + window.storage.reset(); + + // Then anything in the database that isn't a message/conversation/group: + return window.Whisper.Backup.clearStores([ + 'items', + 'identityKeys', + 'sessions', + 'signedPreKeys', + 'preKeys', + 'unprocessed', + ]); + } }; _.extend(SignalProtocolStore.prototype, Backbone.Events); diff --git a/js/storage.js b/js/storage.js index 9820e05f6..c46816594 100644 --- a/js/storage.js +++ b/js/storage.js @@ -67,6 +67,10 @@ console.log('Failed to fetch from storage'); }).always(resolve); }); + }, + + reset: function() { + items.reset(); } }; window.textsecure = window.textsecure || {}; diff --git a/libtextsecure/account_manager.js b/libtextsecure/account_manager.js index 1fd6e6eb0..917517e1d 100644 --- a/libtextsecure/account_manager.js +++ b/libtextsecure/account_manager.js @@ -14,6 +14,19 @@ this.pending = Promise.resolve(); } + function getNumber(numberId) { + if (!numberId || !numberId.length) { + return numberId; + } + + var parts = numberId.split('.'); + if (!parts.length) { + return numberId; + } + + return parts[1]; + } + AccountManager.prototype = new textsecure.EventTarget(); AccountManager.prototype.extend({ constructor: AccountManager, @@ -261,9 +274,29 @@ password = password.substring(0, password.length - 2); var registrationId = libsignal.KeyHelper.generateRegistrationId(); + var previousNumber = getNumber(textsecure.storage.get('number_id')); + return this.server.confirmCode( number, verificationCode, password, signalingKey, registrationId, deviceName ).then(function(response) { + if (previousNumber && previousNumber !== number) { + console.log('New number is different from old number; deleting all previous data'); + + return textsecure.storage.protocol.removeAllData().then(function() { + console.log('Successfully deleted previous data'); + return response; + }, function(error) { + console.log( + 'Something went wrong deleting data from previous number', + error && error.stack ? error.stack : error + ); + + return response; + }); + } + + return response; + }).then(function(response) { textsecure.storage.remove('identityKey'); textsecure.storage.remove('signaling_key'); textsecure.storage.remove('password');