diff --git a/app/sql.js b/app/sql.js index 5164b1f0b..f8dd6ca11 100644 --- a/app/sql.js +++ b/app/sql.js @@ -105,6 +105,7 @@ module.exports = { getAllConversationIds, getAllPrivateConversations, getAllGroupsInvolvingId, + removeAllConversations, searchConversations, searchMessages, @@ -1449,12 +1450,13 @@ async function getSwarmNodesByPubkey(pubkey) { return jsonToObject(row.json).swarmNodes; } +const CONVERSATIONS_TABLE = 'conversations'; async function getConversationCount() { - const row = await db.get('SELECT count(*) from conversations;'); + const row = await db.get(`SELECT count(*) from ${CONVERSATIONS_TABLE};`); if (!row) { throw new Error( - 'getConversationCount: Unable to get count of conversations' + `getConversationCount: Unable to get count of ${CONVERSATIONS_TABLE}` ); } @@ -1474,7 +1476,7 @@ async function saveConversation(data) { } = data; await db.run( - `INSERT INTO conversations ( + `INSERT INTO ${CONVERSATIONS_TABLE} ( id, json, @@ -1538,7 +1540,7 @@ async function updateConversation(data) { } = data; await db.run( - `UPDATE conversations SET + `UPDATE ${CONVERSATIONS_TABLE} SET json = $json, active_at = $active_at, @@ -1564,7 +1566,7 @@ async function updateConversation(data) { async function removeConversation(id) { if (!Array.isArray(id)) { - await db.run('DELETE FROM conversations WHERE id = $id;', { $id: id }); + await db.run(`DELETE FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`, { $id: id }); return; } @@ -1574,7 +1576,7 @@ async function removeConversation(id) { // Our node interface doesn't seem to allow you to replace one single ? with an array await db.run( - `DELETE FROM conversations WHERE id IN ( ${id + `DELETE FROM ${CONVERSATIONS_TABLE} WHERE id IN ( ${id .map(() => '?') .join(', ')} );`, id @@ -1582,7 +1584,7 @@ async function removeConversation(id) { } async function getConversationById(id) { - const row = await db.get('SELECT * FROM conversations WHERE id = $id;', { + const row = await db.get(`SELECT * FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`, { $id: id, }); @@ -1594,13 +1596,13 @@ async function getConversationById(id) { } async function getAllConversations() { - const rows = await db.all('SELECT json FROM conversations ORDER BY id ASC;'); + const rows = await db.all(`SELECT json FROM ${CONVERSATIONS_TABLE} ORDER BY id ASC;`); return map(rows, row => jsonToObject(row.json)); } async function getPubKeysWithFriendStatus(status) { const rows = await db.all( - `SELECT id FROM conversations WHERE + `SELECT id FROM ${CONVERSATIONS_TABLE} WHERE friendRequestStatus = $status ORDER BY id ASC;`, { @@ -1611,13 +1613,13 @@ async function getPubKeysWithFriendStatus(status) { } async function getAllConversationIds() { - const rows = await db.all('SELECT id FROM conversations ORDER BY id ASC;'); + const rows = await db.all(`SELECT id FROM ${CONVERSATIONS_TABLE} ORDER BY id ASC;`); return map(rows, row => row.id); } async function getAllPrivateConversations() { const rows = await db.all( - `SELECT json FROM conversations WHERE + `SELECT json FROM ${CONVERSATIONS_TABLE} WHERE type = 'private' ORDER BY id ASC;` ); @@ -1627,7 +1629,7 @@ async function getAllPrivateConversations() { async function getAllGroupsInvolvingId(id) { const rows = await db.all( - `SELECT json FROM conversations WHERE + `SELECT json FROM ${CONVERSATIONS_TABLE} WHERE type = 'group' AND members LIKE $id ORDER BY id ASC;`, @@ -1641,7 +1643,7 @@ async function getAllGroupsInvolvingId(id) { async function searchConversations(query, { limit } = {}) { const rows = await db.all( - `SELECT json FROM conversations WHERE + `SELECT json FROM ${CONVERSATIONS_TABLE} WHERE ( id LIKE $id OR name LIKE $name OR @@ -2366,6 +2368,10 @@ async function removeAllConfiguration() { await promise; } +async function removeAllConversations() { + await removeAllFromTable(CONVERSATIONS_TABLE); +} + async function getMessagesNeedingUpgrade(limit, { maxVersion }) { const rows = await db.all( `SELECT json FROM messages diff --git a/js/background.js b/js/background.js index 88c2cd755..ac15d198e 100644 --- a/js/background.js +++ b/js/background.js @@ -588,7 +588,7 @@ window.log.info('Import was interrupted, showing import error screen'); appView.openImporter(); } else if ( - Whisper.Registration.everDone() && + Whisper.Registration.isDone() && !Whisper.Registration.ongoingSecondaryDeviceRegistration() ) { // listeners @@ -711,6 +711,12 @@ } }); + Whisper.events.on('showDevicePairingDialog', async () => { + if (appView) { + appView.showDevicePairingDialog(); + } + }); + Whisper.events.on('calculatingPoW', ({ pubKey, timestamp }) => { try { const conversation = ConversationController.get(pubKey); @@ -734,6 +740,15 @@ appView.inboxView.trigger('password-updated'); } }); + + Whisper.events.on('devicePairingRequestAccepted', async (pubKey, cb) => { + try { + await getAccountManager().authoriseSecondaryDevice(pubKey); + cb(null); + } catch (e) { + cb(e); + } + }); } window.getSyncRequest = () => diff --git a/js/modules/data.js b/js/modules/data.js index c0418fb65..cc78b7933 100644 --- a/js/modules/data.js +++ b/js/modules/data.js @@ -177,6 +177,7 @@ module.exports = { removeAll, removeAllConfiguration, + removeAllConversations, removeOtherData, cleanupOrphanedAttachments, @@ -1114,6 +1115,10 @@ async function removeAllConfiguration() { await channels.removeAllConfiguration(); } +async function removeAllConversations() { + await channels.removeAllConversations(); +} + async function cleanupOrphanedAttachments() { await callChannel(CLEANUP_ORPHANED_ATTACHMENTS_KEY); } diff --git a/js/rotate_signed_prekey_listener.js b/js/rotate_signed_prekey_listener.js index 98617ecda..fe0725f29 100644 --- a/js/rotate_signed_prekey_listener.js +++ b/js/rotate_signed_prekey_listener.js @@ -8,6 +8,7 @@ const ROTATION_INTERVAL = 48 * 60 * 60 * 1000; let timeout; let scheduledTime; + let shouldStop = false; function scheduleNextRotation() { const now = Date.now(); @@ -16,6 +17,9 @@ } function run() { + if (shouldStop) { + return; + } window.log.info('Rotating signed prekey...'); getAccountManager() .rotateSignedPreKey() @@ -64,7 +68,11 @@ clearTimeout(timeout); timeout = setTimeout(runWhenOnline, waitTime); } - + function onTimeTravel() { + if (Whisper.Registration.isDone()) { + setTimeoutForNextRun(); + } + } let initComplete; Whisper.RotateSignedPreKeyListener = { init(events, newVersion) { @@ -73,6 +81,7 @@ return; } initComplete = true; + shouldStop = false; if (newVersion) { runWhenOnline(); @@ -80,11 +89,13 @@ setTimeoutForNextRun(); } - events.on('timetravel', () => { - if (Whisper.Registration.isDone()) { - setTimeoutForNextRun(); - } - }); + events.on('timetravel', onTimeTravel); + }, + stop(events) { + initComplete = false; + shouldStop = true; + events.off('timetravel', onTimeTravel); + clearTimeout(timeout); }, }; })(); diff --git a/js/views/standalone_registration_view.js b/js/views/standalone_registration_view.js index cbf91f068..673307a77 100644 --- a/js/views/standalone_registration_view.js +++ b/js/views/standalone_registration_view.js @@ -116,6 +116,8 @@ textsecure.storage.remove('secondaryDeviceStatus'); try { + await this.resetRegistration(); + await window.setPassword(input); await this.accountManager.registerSingleDevice( mnemonic, @@ -141,10 +143,25 @@ Whisper.events.trigger('userChanged', { isSecondaryDevice: true }); this.$el.trigger('openInbox'); }, + async resetRegistration() { + await window.Signal.Data.removeAllIdentityKeys(); + await window.Signal.Data.removeAllPreKeys(); + await window.Signal.Data.removeAllSignedPreKeys(); + await window.Signal.Data.removeAllConversations(); + Whisper.Registration.remove(); + // Do not remove all items since they are only set + // at startup. + textsecure.storage.remove('identityKey') + textsecure.storage.remove('secondaryDeviceStatus'); + window.ConversationController.reset(); + await window.ConversationController.load(); + Whisper.RotateSignedPreKeyListener.stop(Whisper.events); + }, async registerSecondaryDevice() { if (textsecure.storage.get('secondaryDeviceStatus') === 'ongoing') { return; } + await this.resetRegistration(); textsecure.storage.put('secondaryDeviceStatus', 'ongoing'); this.$('#register-secondary-device') .attr('disabled', 'disabled') @@ -169,19 +186,7 @@ this.$('.standalone-secondary-device #error') .text(error) .show(); - const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); - // If the registration started, ensure it's finished - while ( - textsecure.storage.protocol.getIdentityKeyPair() && - !Whisper.Registration.isDone() - ) { - // eslint-disable-next-line no-await-in-loop - await sleep(100); - } - Whisper.Registration.remove(); - Whisper.RotateSignedPreKeyListener.stop(); - textsecure.storage.remove('secondaryDeviceStatus'); - window.ConversationController.reset(); + await this.resetRegistration(); this.$('#register-secondary-device') .removeAttr('disabled') .text('Link'); @@ -198,6 +203,15 @@ 'The primary device has not responded within 1 minute. Ensure that you accept the pairing on the primary device.' ); }; + const c = new Whisper.Conversation({ + id: primaryPubKey, + type: 'private', + }); + const validationError = c.validateNumber(); + if (validationError) { + onError('Invalid public key'); + return; + } try { await this.accountManager.registerSingleDevice( mnemonic,