diff --git a/js/models/messages.js b/js/models/messages.js index 57d6c59b674d..9b658e721467 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -230,9 +230,11 @@ const unidentifiedLookup = ( this.get('unidentifiedDeliveries') || [] - ).reduce((accumulator, item) => { + ).reduce((accumulator, uuidOrE164) => { // eslint-disable-next-line no-param-reassign - accumulator[item] = true; + accumulator[ + ConversationController.getConversationId(uuidOrE164) + ] = true; return accumulator; }, Object.create(null)); diff --git a/js/modules/data.js b/js/modules/data.js index e23258012ca1..c266a86dcb6d 100644 --- a/js/modules/data.js +++ b/js/modules/data.js @@ -180,8 +180,22 @@ module.exports = { }; // When IPC arguments are prepared for the cross-process send, they are JSON.stringified. -// We can't send ArrayBuffers or BigNumbers (what we get from proto library for dates). -function _cleanData(data) { +// We can't send ArrayBuffers or BigNumbers (what we get from proto library for dates), +// We also cannot send objects with function-value keys, like what protobufjs gives us. +function _cleanData(data, path = 'root') { + if (data === null || data === undefined) { + window.log.warn(`_cleanData: null or undefined value at path ${path}`); + return data; + } + + if ( + typeof data === 'string' || + typeof data === 'number' || + typeof data === 'boolean' + ) { + return data; + } + const keys = Object.keys(data); for (let index = 0, max = keys.length; index < max; index += 1) { const key = keys[index]; @@ -192,15 +206,21 @@ function _cleanData(data) { continue; } - if (isFunction(value.toNumber)) { + if (isFunction(value)) { + // To prepare for Electron v9 IPC, we need to take functions off of any object + // eslint-disable-next-line no-param-reassign + delete data[key]; + } else if (isFunction(value.toNumber)) { // eslint-disable-next-line no-param-reassign data[key] = value.toNumber(); } else if (Array.isArray(value)) { // eslint-disable-next-line no-param-reassign - data[key] = value.map(item => _cleanData(item)); + data[key] = value.map((item, mapIndex) => + _cleanData(item, `${path}.${key}.${mapIndex}`) + ); } else if (isObject(value)) { // eslint-disable-next-line no-param-reassign - data[key] = _cleanData(value); + data[key] = _cleanData(value, `${path}.${key}`); } else if ( typeof value !== 'string' && typeof value !== 'number' && @@ -209,6 +229,7 @@ function _cleanData(data) { window.log.info(`_cleanData: key ${key} had type ${typeof value}`); } } + return data; } @@ -352,19 +373,25 @@ function makeChannel(fnName) { const jobId = _makeJob(fnName); return new Promise((resolve, reject) => { - ipcRenderer.send(SQL_CHANNEL_KEY, jobId, fnName, ...args); + try { + ipcRenderer.send(SQL_CHANNEL_KEY, jobId, fnName, ...args); - _updateJob(jobId, { - resolve, - reject, - args: _DEBUG ? args : null, - }); + _updateJob(jobId, { + resolve, + reject, + args: _DEBUG ? args : null, + }); - setTimeout( - () => - reject(new Error(`SQL channel job ${jobId} (${fnName}) timed out`)), - DATABASE_UPDATE_TIMEOUT - ); + setTimeout( + () => + reject(new Error(`SQL channel job ${jobId} (${fnName}) timed out`)), + DATABASE_UPDATE_TIMEOUT + ); + } catch (error) { + _removeJob(jobId); + + reject(error); + } }); }; } @@ -515,16 +542,8 @@ async function removeAllSignedPreKeys() { const ITEM_KEYS = { identityKey: ['value.pubKey', 'value.privKey'], - senderCertificate: [ - 'value.certificate', - 'value.signature', - 'value.serialized', - ], - senderCertificateWithUuid: [ - 'value.certificate', - 'value.signature', - 'value.serialized', - ], + senderCertificate: ['value.serialized'], + senderCertificateWithUuid: ['value.serialized'], signaling_key: ['value'], profileKey: ['value'], }; @@ -981,7 +1000,7 @@ async function getNextAttachmentDownloadJobs(limit) { return channels.getNextAttachmentDownloadJobs(limit); } async function saveAttachmentDownloadJob(job) { - await channels.saveAttachmentDownloadJob(job); + await channels.saveAttachmentDownloadJob(_cleanData(job)); } async function setAttachmentDownloadJobPending(id, pending) { await channels.setAttachmentDownloadJobPending(id, pending); diff --git a/js/modules/refresh_sender_certificate.js b/js/modules/refresh_sender_certificate.js index 708b055108fb..ac07fc21ffdd 100644 --- a/js/modules/refresh_sender_certificate.js +++ b/js/modules/refresh_sender_certificate.js @@ -43,28 +43,27 @@ function initialize({ events, storage, navigator, logger }) { function scheduleNextRotation() { const now = Date.now(); const certificate = storage.get('senderCertificate'); - if (!certificate) { + if (!certificate || !certificate.expires) { setTimeoutForNextRun(scheduledTime || now); return; } - // The useful information in a SenderCertificate is all serialized, so we - // need to do another layer of decoding. - const decoded = textsecure.protobuf.SenderCertificate.Certificate.decode( - certificate.certificate - ); - const expires = decoded.expires.toNumber(); - // If we have a time in place and it's already before the safety zone before expire, // we keep it - if (scheduledTime && scheduledTime <= expires - MINIMUM_TIME_LEFT) { + if ( + scheduledTime && + scheduledTime <= certificate.expires - MINIMUM_TIME_LEFT + ) { setTimeoutForNextRun(scheduledTime); return; } // Otherwise, we reset every day, or earlier if the safety zone requires it - const time = Math.min(now + ONE_DAY, expires - MINIMUM_TIME_LEFT); + const time = Math.min( + now + ONE_DAY, + certificate.expires - MINIMUM_TIME_LEFT + ); setTimeoutForNextRun(time); } @@ -88,18 +87,21 @@ function initialize({ events, storage, navigator, logger }) { const arrayBuffer = window.Signal.Crypto.base64ToArrayBuffer( certificate ); - const decoded = textsecure.protobuf.SenderCertificate.decode( + const decodedContainer = textsecure.protobuf.SenderCertificate.decode( arrayBuffer ); - - decoded.certificate = decoded.certificate.toArrayBuffer(); - decoded.signature = decoded.signature.toArrayBuffer(); - decoded.serialized = arrayBuffer; - - storage.put( - `senderCertificate${withUuid ? 'WithUuid' : ''}`, - decoded + const decodedCert = textsecure.protobuf.SenderCertificate.Certificate.decode( + decodedContainer.certificate ); + + // We don't want to send a protobuf-generated object across IPC, so we make + // our own object. + const toSave = { + expires: decodedCert.expires.toNumber(), + serialized: arrayBuffer, + }; + + storage.put(`senderCertificate${withUuid ? 'WithUuid' : ''}`, toSave); }) ); diff --git a/main.js b/main.js index f47f5ebfcb08..520974eca3d0 100644 --- a/main.js +++ b/main.js @@ -41,6 +41,10 @@ console.log('Set Windows Application User Model ID (AUMID)', { }); app.setAppUserModelId(appUserModelId); +// We don't navigate, but this is the way of the future +// https://github.com/electron/electron/issues/18397 +app.allowRendererProcessReuse = true; + // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. let mainWindow; @@ -250,7 +254,6 @@ function createWindow() { config.environment === 'test' || config.environment === 'test-lib' ? '#ffffff' // Tests should always be rendered on a white background : '#3a76f0', - vibrancy: 'appearance-based', webPreferences: { nodeIntegration: false, nodeIntegrationInWorker: false, @@ -311,7 +314,7 @@ function createWindow() { // so if we need to recreate the window, we have the most recent settings windowConfig = { maximized: mainWindow.isMaximized(), - autoHideMenuBar: mainWindow.isMenuBarAutoHide(), + autoHideMenuBar: mainWindow.autoHideMenuBar, fullscreen: mainWindow.isFullScreen(), width: size[0], height: size[1], @@ -496,7 +499,6 @@ function showAbout() { autoHideMenuBar: true, backgroundColor: '#3a76f0', show: false, - vibrancy: 'appearance-based', webPreferences: { nodeIntegration: false, nodeIntegrationInWorker: false, @@ -545,7 +547,6 @@ async function showSettingsWindow() { backgroundColor: '#3a76f0', show: false, modal: true, - vibrancy: 'appearance-based', webPreferences: { nodeIntegration: false, nodeIntegrationInWorker: false, @@ -665,7 +666,6 @@ async function showDebugLogWindow() { backgroundColor: '#3a76f0', show: false, modal: true, - vibrancy: 'appearance-based', webPreferences: { nodeIntegration: false, nodeIntegrationInWorker: false, @@ -714,7 +714,6 @@ async function showPermissionsPopupWindow() { backgroundColor: '#3a76f0', show: false, modal: true, - vibrancy: 'appearance-based', webPreferences: { nodeIntegration: false, nodeIntegrationInWorker: false, @@ -990,7 +989,7 @@ app.on('will-finish-launching', () => { }); ipc.on('set-badge-count', (event, count) => { - app.setBadgeCount(count); + app.badgeCount = count; }); ipc.on('remove-setup-menu-items', () => { @@ -1023,7 +1022,7 @@ ipc.on('restart', () => { ipc.on('set-auto-hide-menu-bar', (event, autoHide) => { if (mainWindow) { - mainWindow.setAutoHideMenuBar(autoHide); + mainWindow.autoHideMenuBar = autoHide; } }); diff --git a/package.json b/package.json index bffe46f768e2..c07221390583 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "classnames": "2.2.5", "config": "1.28.1", "copy-text-to-clipboard": "2.1.0", - "curve25519-n": "https://github.com/scottnonnenberg-signal/node-curve25519.git#1bd0580843dcf836284dee7f1c4dfb4c698f7969", + "curve25519-n": "https://github.com/scottnonnenberg-signal/node-curve25519.git#3e94f60bc54b2426476520d8d1a0aa835c25f5cc", "draft-js": "0.10.5", "electron-context-menu": "0.11.0", "electron-editor-context-menu": "1.1.1", diff --git a/permissions_popup_preload.js b/permissions_popup_preload.js index 072406793dcd..f895244af0b7 100644 --- a/permissions_popup_preload.js +++ b/permissions_popup_preload.js @@ -5,7 +5,7 @@ const url = require('url'); const i18n = require('./js/modules/i18n'); const { makeGetter, makeSetter } = require('./preload_utils'); -const { systemPreferences } = remote.require('electron'); +const { nativeTheme } = remote.require('electron'); const config = url.parse(window.location.toString(), true).query; const { locale } = config; @@ -17,22 +17,16 @@ window.theme = config.theme; window.i18n = i18n.setup(locale, localeMessages); function setSystemTheme() { - window.systemTheme = systemPreferences.isDarkMode() ? 'dark' : 'light'; + window.systemTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light'; } setSystemTheme(); window.subscribeToSystemThemeChange = fn => { - if (!systemPreferences.subscribeNotification) { - return; - } - systemPreferences.subscribeNotification( - 'AppleInterfaceThemeChangedNotification', - () => { - setSystemTheme(); - fn(); - } - ); + nativeTheme.on('updated', () => { + setSystemTheme(); + fn(); + }); }; require('./js/logging'); diff --git a/preload.js b/preload.js index b81a94ad7777..357d8f19094d 100644 --- a/preload.js +++ b/preload.js @@ -13,7 +13,7 @@ try { const { remote } = electron; const { app } = remote; - const { systemPreferences } = remote.require('electron'); + const { nativeTheme } = remote.require('electron'); window.PROTO_ROOT = 'protos'; const config = require('url').parse(window.location.toString(), true).query; @@ -38,22 +38,16 @@ try { window.isBehindProxy = () => Boolean(config.proxyUrl); function setSystemTheme() { - window.systemTheme = systemPreferences.isDarkMode() ? 'dark' : 'light'; + window.systemTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light'; } setSystemTheme(); window.subscribeToSystemThemeChange = fn => { - if (!systemPreferences.subscribeNotification) { - return; - } - systemPreferences.subscribeNotification( - 'AppleInterfaceThemeChangedNotification', - () => { - setSystemTheme(); - fn(); - } - ); + nativeTheme.on('updated', () => { + setSystemTheme(); + fn(); + }); }; window.isBeforeVersion = (toCheck, baseVersion) => { diff --git a/settings_preload.js b/settings_preload.js index 9018fa1ca683..23b6b60083f9 100644 --- a/settings_preload.js +++ b/settings_preload.js @@ -9,29 +9,23 @@ const config = url.parse(window.location.toString(), true).query; const { locale } = config; const localeMessages = ipcRenderer.sendSync('locale-data'); -const { systemPreferences } = remote.require('electron'); +const { nativeTheme } = remote.require('electron'); window.platform = process.platform; window.theme = config.theme; window.i18n = i18n.setup(locale, localeMessages); function setSystemTheme() { - window.systemTheme = systemPreferences.isDarkMode() ? 'dark' : 'light'; + window.systemTheme = nativeTheme.shouldUseDarkColors ? 'dark' : 'light'; } setSystemTheme(); window.subscribeToSystemThemeChange = fn => { - if (!systemPreferences.subscribeNotification) { - return; - } - systemPreferences.subscribeNotification( - 'AppleInterfaceThemeChangedNotification', - () => { - setSystemTheme(); - fn(); - } - ); + nativeTheme.on('updated', () => { + setSystemTheme(); + fn(); + }); }; window.getEnvironment = () => config.environment; diff --git a/sticker-creator/preload.js b/sticker-creator/preload.js index 06b291bf7837..666f392cc21a 100644 --- a/sticker-creator/preload.js +++ b/sticker-creator/preload.js @@ -10,7 +10,7 @@ const { deriveStickerPackKey } = require('../js/modules/crypto'); const { makeGetter } = require('../preload_utils'); const { dialog } = remote; -const { systemPreferences } = remote.require('electron'); +const { nativeTheme } = remote.require('electron'); window.ROOT_PATH = window.location.href.startsWith('file') ? '../../' : '/'; window.PROTO_ROOT = '../../protos'; @@ -157,7 +157,7 @@ const getThemeSetting = makeGetter('theme-setting'); async function resolveTheme() { const theme = (await getThemeSetting()) || 'light'; if (process.platform === 'darwin' && theme === 'system') { - return systemPreferences.isDarkMode() ? 'dark' : 'light'; + return nativeTheme.shouldUseDarkColors ? 'dark' : 'light'; } return theme; } @@ -170,11 +170,8 @@ async function applyTheme() { window.addEventListener('DOMContentLoaded', applyTheme); -if (systemPreferences && systemPreferences.subscribeNotification) { - systemPreferences.subscribeNotification( - 'AppleInterfaceThemeChangedNotification', - applyTheme - ); -} +nativeTheme.on('updated', () => { + applyTheme(); +}); window.log.info('sticker-creator preload complete...'); diff --git a/test/metadata/SecretSessionCipher_test.js b/test/metadata/SecretSessionCipher_test.js index e035e594d5f2..f73b478889d0 100644 --- a/test/metadata/SecretSessionCipher_test.js +++ b/test/metadata/SecretSessionCipher_test.js @@ -170,7 +170,7 @@ describe('SecretSessionCipher', () => { const ciphertext = await aliceCipher.encrypt( new libsignal.SignalProtocolAddress('+14152222222', 1), - senderCertificate, + { serialized: senderCertificate.serialized }, bytesFromString('smert za smert') ); @@ -212,7 +212,7 @@ describe('SecretSessionCipher', () => { const ciphertext = await aliceCipher.encrypt( new libsignal.SignalProtocolAddress('+14152222222', 1), - senderCertificate, + { serialized: senderCertificate.serialized }, bytesFromString('и вот я') ); @@ -252,7 +252,7 @@ describe('SecretSessionCipher', () => { const ciphertext = await aliceCipher.encrypt( new libsignal.SignalProtocolAddress('+14152222222', 1), - senderCertificate, + { serialized: senderCertificate.serialized }, bytesFromString('и вот я') ); @@ -291,7 +291,7 @@ describe('SecretSessionCipher', () => { const ciphertext = await aliceCipher.encrypt( new libsignal.SignalProtocolAddress('+14152222222', 1), - senderCertificate, + { serialized: senderCertificate.serialized }, bytesFromString('smert za smert') ); diff --git a/yarn.lock b/yarn.lock index 5de9c2dac54b..23ab63ec1a0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5543,9 +5543,9 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" -"curve25519-n@https://github.com/scottnonnenberg-signal/node-curve25519.git#1bd0580843dcf836284dee7f1c4dfb4c698f7969": +"curve25519-n@https://github.com/scottnonnenberg-signal/node-curve25519.git#3e94f60bc54b2426476520d8d1a0aa835c25f5cc": version "1.5.0" - resolved "https://github.com/scottnonnenberg-signal/node-curve25519.git#1bd0580843dcf836284dee7f1c4dfb4c698f7969" + resolved "https://github.com/scottnonnenberg-signal/node-curve25519.git#3e94f60bc54b2426476520d8d1a0aa835c25f5cc" dependencies: bindings "^1.5.0" nan "^2.14.0"