Merge pull request #40 from Mikunj/merge/signal

Merge Signal 1.18.0
pull/42/head
sachaaaaa 6 years ago committed by GitHub
commit 9f101d9381
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -272,6 +272,10 @@
const clearDataView = new window.Whisper.ClearDataView().render();
$('body').append(clearDataView.el);
},
shutdown: async () => {
await window.Signal.Data.shutdown();
},
};
const currentVersion = window.getVersion();
@ -297,10 +301,18 @@
await mandatoryMessageUpgrade({ upgradeMessageSchema });
await migrateAllToSQLCipher({ writeNewAttachmentData, Views });
await removeDatabase();
await window.Signal.Data.removeIndexedDBFiles();
try {
await window.Signal.Data.removeIndexedDBFiles();
} catch (error) {
window.log.error(
'Failed to remove IndexedDB files:',
error && error.stack ? error.stack : error
);
}
window.installStorage(window.newStorage);
await window.storage.fetch();
await storage.put('indexeddb-delete-needed', true);
}
Views.Initialization.setMessage(window.i18n('optimizingApplication'));

@ -899,7 +899,13 @@
return this.OUR_NUMBER;
},
getContact() {
return ConversationController.getOrCreate(this.getSource(), 'private');
const source = this.getSource();
if (!source) {
return null;
}
return ConversationController.getOrCreate(source, 'private');
},
isOutgoing() {
return this.get('type') === 'outgoing';

@ -32,6 +32,9 @@ const CLEANUP_ORPHANED_ATTACHMENTS_KEY = 'cleanup-orphaned-attachments';
const _jobs = Object.create(null);
const _DEBUG = false;
let _jobCounter = 0;
let _shuttingDown = false;
let _shutdownCallback = null;
let _shutdownPromise = null;
const channels = {};
@ -39,6 +42,7 @@ module.exports = {
_jobs,
_cleanData,
shutdown,
close,
removeDB,
removeIndexedDBFiles,
@ -190,7 +194,45 @@ function _cleanData(data) {
return data;
}
async function _shutdown() {
if (_shutdownPromise) {
return _shutdownPromise;
}
_shuttingDown = true;
const jobKeys = Object.keys(_jobs);
window.log.info(
`data.shutdown: starting process. ${jobKeys.length} jobs outstanding`
);
// No outstanding jobs, return immediately
if (jobKeys.length === 0) {
return null;
}
// Outstanding jobs; we need to wait until the last one is done
_shutdownPromise = new Promise((resolve, reject) => {
_shutdownCallback = error => {
window.log.info('data.shutdown: process complete');
if (error) {
return reject(error);
}
return resolve();
};
});
return _shutdownPromise;
}
function _makeJob(fnName) {
if (_shuttingDown && fnName !== 'close') {
throw new Error(
`Rejecting SQL channel job (${fnName}); application is shutting down`
);
}
_jobCounter += 1;
const id = _jobCounter;
@ -237,8 +279,16 @@ function _updateJob(id, data) {
function _removeJob(id) {
if (_DEBUG) {
_jobs[id].complete = true;
} else {
delete _jobs[id];
return;
}
delete _jobs[id];
if (_shutdownCallback) {
const keys = Object.keys(_jobs);
if (keys.length === 0) {
_shutdownCallback();
}
}
}
@ -328,6 +378,14 @@ function keysFromArrayBuffer(keys, data) {
// Top-level calls
async function shutdown() {
// Stop accepting new SQL jobs, flush outstanding queue
await _shutdown();
// Close database
await close();
}
// Note: will need to restart the app after calling this, to set up afresh
async function close() {
await channels.close();

@ -7,9 +7,9 @@ module.exports = {
const ONE_DAY = 24 * 60 * 60 * 1000; // one day
const MINIMUM_TIME_LEFT = 2 * 60 * 60 * 1000; // two hours
let initialized = false;
let timeout = null;
let scheduledTime = null;
let scheduleNext = null;
// We need to refresh our own profile regularly to account for newly-added devices which
// do not support unidentified delivery.
@ -20,19 +20,16 @@ function refreshOurProfile() {
}
function initialize({ events, storage, navigator, logger }) {
if (initialized) {
logger.warn('refreshSenderCertificate: already initialized!');
// We don't want to set up all of the below functions, but we do want to ensure that our
// refresh timer is up-to-date.
if (scheduleNext) {
scheduleNext();
return;
}
initialized = true;
runWhenOnline();
events.on('timetravel', () => {
if (initialized) {
scheduleNextRotation();
}
});
events.on('timetravel', scheduleNextRotation);
function scheduleNextRotation() {
const now = Date.now();
@ -55,6 +52,9 @@ function initialize({ events, storage, navigator, logger }) {
setTimeoutForNextRun(time);
}
// Keeping this entrypoint around so more inialize() calls just kick the timing
scheduleNext = scheduleNextRotation;
async function run() {
logger.info('refreshSenderCertificate: Getting new certificate...');
try {

@ -167,6 +167,7 @@ function _createSocket(url, { certificateAuthority, proxyUrl, signature }) {
return new WebSocket(url, null, null, headers, requestOptions);
}
const FIVE_MINUTES = 1000 * 60 * 5;
const agents = {
unauth: null,
auth: null,
@ -182,16 +183,21 @@ function _promiseAjax(providedUrl, options) {
typeof options.timeout !== 'undefined' ? options.timeout : 10000;
const { proxyUrl } = options;
const agentType = options.unathenticated ? 'unauth' : 'auth';
const agentType = options.unauthenticated ? 'unauth' : 'auth';
if (!agents[agentType]) {
if (proxyUrl) {
agents[agentType] = new ProxyAgent(proxyUrl);
} else {
agents[agentType] = new Agent();
const { timestamp } = agents[agentType] || {};
if (!timestamp || timestamp + FIVE_MINUTES < Date.now()) {
if (timestamp) {
log.info(`Cycling agent for type ${agentType}`);
}
agents[agentType] = {
agent: proxyUrl
? new ProxyAgent(proxyUrl)
: new Agent({ keepAlive: true }),
timestamp: Date.now(),
};
}
const agent = agents[agentType];
const { agent } = agents[agentType];
const fetchOptions = {
method: options.type,
@ -443,7 +449,7 @@ function initialize({ url, cdnUrl, certificateAuthority, proxyUrl }) {
message =
'The server rejected our query, please file a bug report.';
}
e.message = message;
e.message = `${message} (original: ${e.message})`;
throw e;
});
}

@ -317,19 +317,21 @@
identityKeyPair.pubKey
);
return Promise.resolve().then(() => {
textsecure.storage.remove('identityKey');
textsecure.storage.remove('signaling_key');
textsecure.storage.remove('password');
textsecure.storage.remove('registrationId');
textsecure.storage.remove('number_id');
textsecure.storage.remove('device_name');
textsecure.storage.remove('userAgent');
textsecure.storage.remove('read-receipts-setting');
return Promise.resolve().then(async () => {
await Promise.all([
textsecure.storage.remove('identityKey'),
textsecure.storage.remove('signaling_key'),
textsecure.storage.remove('password'),
textsecure.storage.remove('registrationId'),
textsecure.storage.remove('number_id'),
textsecure.storage.remove('device_name'),
textsecure.storage.remove('userAgent'),
textsecure.storage.remove('read-receipts-setting'),
]);
// update our own identity key, which may have changed
// if we're relinking after a reinstall on the master device
textsecure.storage.protocol.saveIdentityWithAttributes(pubKeyString, {
await textsecure.storage.protocol.saveIdentityWithAttributes(pubKeyString, {
id: pubKeyString,
publicKey: identityKeyPair.pubKey,
firstUse: true,
@ -338,20 +340,20 @@
nonblockingApproval: true,
});
textsecure.storage.put('identityKey', identityKeyPair);
textsecure.storage.put('signaling_key', signalingKey);
textsecure.storage.put('password', password);
textsecure.storage.put('registrationId', registrationId);
await textsecure.storage.put('identityKey', identityKeyPair);
await textsecure.storage.put('signaling_key', signalingKey);
await textsecure.storage.put('password', password);
await textsecure.storage.put('registrationId', registrationId);
if (userAgent) {
textsecure.storage.put('userAgent', userAgent);
}
if (readReceipts) {
textsecure.storage.put('read-receipt-setting', true);
} else {
textsecure.storage.put('read-receipt-setting', false);
await textsecure.storage.put('userAgent', userAgent);
}
textsecure.storage.user.setNumberAndDeviceId(pubKeyString, 1);
await textsecure.storage.put(
'read-receipt-setting',
Boolean(readReceipts)
);
await textsecure.storage.user.setNumberAndDeviceId(pubKeyString, 1);
});
},
clearSessionsAndPreKeys() {

@ -304,6 +304,9 @@ MessageReceiver.prototype.extend({
}
envelope.id = envelope.serverGuid || window.getGuid();
envelope.serverTimestamp = envelope.serverTimestamp
? envelope.serverTimestamp.toNumber()
: null;
return this.addToCache(envelope, plaintext).then(
async () => {
@ -421,6 +424,11 @@ MessageReceiver.prototype.extend({
);
}
const envelope = textsecure.protobuf.Envelope.decode(envelopePlaintext);
envelope.id = envelope.serverGuid || item.id;
envelope.source = envelope.source || item.source;
envelope.sourceDevice = envelope.sourceDevice || item.sourceDevice;
envelope.serverTimestamp =
envelope.serverTimestamp || item.serverTimestamp;
const { decrypted } = item;
if (decrypted) {
@ -533,15 +541,19 @@ MessageReceiver.prototype.extend({
}
if (item.get('version') === 2) {
item.set(
'decrypted',
await MessageReceiver.arrayBufferToStringBase64(plaintext)
);
item.set({
source: envelope.source,
sourceDevice: envelope.sourceDevice,
serverTimestamp: envelope.serverTimestamp,
decrypted: await MessageReceiver.arrayBufferToStringBase64(plaintext),
});
} else {
item.set(
'decrypted',
await MessageReceiver.arrayBufferToString(plaintext)
);
item.set({
source: envelope.source,
sourceDevice: envelope.sourceDevice,
serverTimestamp: envelope.serverTimestamp,
decrypted: await MessageReceiver.arrayBufferToString(plaintext),
});
}
return textsecure.storage.unprocessed.save(item.attributes);
@ -718,12 +730,7 @@ MessageReceiver.prototype.extend({
.decrypt(
window.Signal.Metadata.createCertificateValidator(serverTrustRoot),
ciphertext.toArrayBuffer(),
Math.min(
envelope.serverTimestamp
? envelope.serverTimestamp.toNumber()
: Date.now(),
Date.now()
),
Math.min(envelope.serverTimestamp || Date.now(), Date.now()),
me
)
.then(
@ -764,7 +771,7 @@ MessageReceiver.prototype.extend({
throw error;
}
return this.removeFromCache().then(() => {
return this.removeFromCache(envelope).then(() => {
throw error;
});
}

@ -330,27 +330,43 @@ function createWindow() {
captureClicks(mainWindow);
// Emitted when the window is about to be closed.
mainWindow.on('close', e => {
// Note: We do most of our shutdown logic here because all windows are closed by
// Electron before the app quits.
mainWindow.on('close', async e => {
console.log('close event', {
readyForShutdown: mainWindow ? mainWindow.readyForShutdown : null,
shouldQuit: windowState.shouldQuit(),
});
// If the application is terminating, just do the default
if (
windowState.shouldQuit() ||
config.environment === 'test' ||
config.environment === 'test-lib'
config.environment === 'test-lib' ||
(mainWindow.readyForShutdown && windowState.shouldQuit())
) {
return;
}
// Prevent the shutdown
e.preventDefault();
mainWindow.hide();
// On Mac, or on other platforms when the tray icon is in use, the window
// should be only hidden, not closed, when the user clicks the close button
if (usingTrayIcon || process.platform === 'darwin') {
e.preventDefault();
mainWindow.hide();
if (
!windowState.shouldQuit() &&
(usingTrayIcon || process.platform === 'darwin')
) {
// toggle the visibility of the show/hide tray icon menu entries
if (tray) {
tray.updateContextMenu();
}
return;
}
await requestShutdown();
mainWindow.readyForShutdown = true;
app.quit();
});
// Emitted when the window is closed.
@ -641,6 +657,20 @@ app.on('ready', async () => {
await sql.initialize({ configDir: userDataPath, key });
await sqlChannels.initialize();
try {
const IDB_KEY = 'indexeddb-delete-needed';
const item = await sql.getItemById(IDB_KEY);
if (item && item.value) {
await sql.removeIndexedDBFiles();
await sql.removeItemById(IDB_KEY);
}
} catch (error) {
console.log(
'(ready event handler) error deleting IndexedDB:',
error && error.stack ? error.stack : error
);
}
async function cleanupOrphanedAttachments() {
const allAttachments = await attachments.getAllAttachments(userDataPath);
const orphanedAttachments = await sql.removeKnownAttachments(
@ -692,7 +722,51 @@ function setupMenu(options) {
Menu.setApplicationMenu(menu);
}
async function requestShutdown() {
if (!mainWindow || !mainWindow.webContents) {
return;
}
console.log('requestShutdown: Requesting close of mainWindow...');
const request = new Promise((resolve, reject) => {
ipc.once('now-ready-for-shutdown', (_event, error) => {
console.log('requestShutdown: Response received');
if (error) {
return reject(error);
}
return resolve();
});
mainWindow.webContents.send('get-ready-for-shutdown');
// We'll wait two minutes, then force the app to go down. This can happen if someone
// exits the app before we've set everything up in preload() (so the browser isn't
// yet listening for these events), or if there are a whole lot of stacked-up tasks.
// Note: two minutes is also our timeout for SQL tasks in data.js in the browser.
setTimeout(() => {
console.log(
'requestShutdown: Response never received; forcing shutdown.'
);
resolve();
}, 2 * 60 * 1000);
});
try {
await request;
} catch (error) {
console.log(
'requestShutdown error:',
error && error.stack ? error.stack : error
);
}
}
app.on('before-quit', () => {
console.log('before-quit event', {
readyForShutdown: mainWindow ? mainWindow.readyForShutdown : null,
shouldQuit: windowState.shouldQuit(),
});
windowState.markShouldQuit();
});

@ -3,7 +3,7 @@
"productName": "Loki Messenger",
"description": "Private messaging from your desktop",
"repository": "https://github.com/sloki-project/loki-messenger.git",
"version": "1.17.0",
"version": "1.18.0",
"license": "GPL-3.0",
"author": {
"name": "Open Whisper Systems",

@ -151,6 +151,25 @@ ipc.on('delete-all-data', () => {
}
});
ipc.on('get-ready-for-shutdown', async () => {
const { shutdown } = window.Events;
if (!shutdown) {
window.log.error('preload shutdown handler: shutdown method not found');
ipc.send('now-ready-for-shutdown');
return;
}
try {
await shutdown();
ipc.send('now-ready-for-shutdown');
} catch (error) {
ipc.send(
'now-ready-for-shutdown',
error && error.stack ? error.stack : error
);
}
});
function installGetter(name, functionName) {
ipc.on(`get-${name}`, async () => {
const getFn = window.Events[functionName];
@ -159,7 +178,10 @@ function installGetter(name, functionName) {
try {
ipc.send(`get-success-${name}`, null, await getFn());
} catch (error) {
ipc.send(`get-success-${name}`, error);
ipc.send(
`get-success-${name}`,
error && error.stack ? error.stack : error
);
}
}
});

@ -227,7 +227,7 @@
"rule": "jQuery-load(",
"path": "js/background.js",
"line": " await ConversationController.load();",
"lineNumber": 405,
"lineNumber": 417,
"reasonCategory": "falseMatch",
"updated": "2018-10-02T21:00:44.007Z"
},
@ -235,7 +235,7 @@
"rule": "jQuery-$(",
"path": "js/background.js",
"line": " el: $('body'),",
"lineNumber": 468,
"lineNumber": 480,
"reasonCategory": "usageTrusted",
"updated": "2018-10-16T23:47:48.006Z",
"reasonDetail": "Protected from arbitrary input"
@ -244,7 +244,7 @@
"rule": "jQuery-wrap(",
"path": "js/background.js",
"line": " wrap(",
"lineNumber": 727,
"lineNumber": 739,
"reasonCategory": "falseMatch",
"updated": "2018-10-18T22:23:00.485Z"
},
@ -252,7 +252,7 @@
"rule": "jQuery-wrap(",
"path": "js/background.js",
"line": " await wrap(",
"lineNumber": 1228,
"lineNumber": 1240,
"reasonCategory": "falseMatch",
"updated": "2018-10-26T22:43:23.229Z"
},
@ -311,7 +311,7 @@
"rule": "jQuery-wrap(",
"path": "js/models/messages.js",
"line": " return wrap(",
"lineNumber": 994,
"lineNumber": 1000,
"reasonCategory": "falseMatch",
"updated": "2018-10-05T23:12:28.961Z"
},
@ -2315,7 +2315,7 @@
"rule": "jQuery-wrap(",
"path": "libtextsecure/message_receiver.js",
"line": " const buffer = dcodeIO.ByteBuffer.wrap(ciphertext);",
"lineNumber": 774,
"lineNumber": 781,
"reasonCategory": "falseMatch",
"updated": "2018-09-19T18:13:29.628Z"
},
@ -2323,7 +2323,7 @@
"rule": "jQuery-wrap(",
"path": "libtextsecure/message_receiver.js",
"line": " const buffer = dcodeIO.ByteBuffer.wrap(ciphertext);",
"lineNumber": 799,
"lineNumber": 806,
"reasonCategory": "falseMatch",
"updated": "2018-09-19T18:13:29.628Z"
},

Loading…
Cancel
Save