increase prettier maxWidth to 100

pull/1576/head
Audric Ackermann 3 years ago
parent 6896cffd75
commit f7581cf4eb
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -46,11 +46,7 @@ module.exports = {
// Use LF to stay consistent
'linebreak-style': ['error', 'unix'],
quotes: [
'error',
'single',
{ avoidEscape: true, allowTemplateLiterals: true },
],
quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: true }],
// Prettier overrides:
'arrow-parens': 'off',

@ -3,4 +3,5 @@ module.exports = {
trailingComma: 'es5',
bracketSpacing: true,
arrowParens: 'avoid',
printWidth: 100,
};

@ -59,11 +59,7 @@ module.exports = grunt => {
dest: 'libloki/test/components.js',
},
test: {
src: [
'node_modules/mocha/mocha.js',
'node_modules/chai/chai.js',
'test/_test.js',
],
src: ['node_modules/mocha/mocha.js', 'node_modules/chai/chai.js', 'test/_test.js'],
dest: 'test/test.js',
},
// TODO: Move errors back down?
@ -90,19 +86,11 @@ module.exports = grunt => {
dest: 'js/libtextsecure.js',
},
libloki: {
src: [
'libloki/crypto.js',
'libloki/service_nodes.js',
'libloki/storage.js',
],
src: ['libloki/crypto.js', 'libloki/service_nodes.js', 'libloki/storage.js'],
dest: 'js/libloki.js',
},
lokitest: {
src: [
'node_modules/mocha/mocha.js',
'node_modules/chai/chai.js',
'libloki/test/_test.js',
],
src: ['node_modules/mocha/mocha.js', 'node_modules/chai/chai.js', 'libloki/test/_test.js'],
dest: 'libloki/test/test.js',
},
libtextsecuretest: {
@ -155,12 +143,7 @@ module.exports = grunt => {
tasks: ['sass'],
},
transpile: {
files: [
'./ts/**/*.ts',
'./ts/**/*.tsx',
'./ts/**/**/*.tsx',
'./test/ts/**.ts',
],
files: ['./ts/**/*.ts', './ts/**/*.tsx', './ts/**/**/*.tsx', './test/ts/**.ts'],
tasks: ['exec:transpile'],
},
},
@ -222,10 +205,7 @@ module.exports = grunt => {
// eslint-disable-next-line no-restricted-syntax
for (const key in messages) {
if (en[key] !== undefined && messages[key] !== undefined) {
if (
en[key].placeholders !== undefined &&
messages[key].placeholders === undefined
) {
if (en[key].placeholders !== undefined && messages[key].placeholders === undefined) {
messages[key].placeholders = en[key].placeholders;
}
}
@ -274,8 +254,7 @@ module.exports = grunt => {
function runTests(environment, cb) {
let failure;
const { Application } = spectron;
const electronBinary =
process.platform === 'win32' ? 'electron.cmd' : 'electron';
const electronBinary = process.platform === 'win32' ? 'electron.cmd' : 'electron';
const app = new Application({
path: path.join(__dirname, 'node_modules', '.bin', electronBinary),
args: [path.join(__dirname, 'main.js')],
@ -284,9 +263,7 @@ module.exports = grunt => {
},
requireName: 'unused',
chromeDriverArgs: [
`remote-debugging-port=${Math.floor(
Math.random() * (9999 - 9000) + 9000
)}`,
`remote-debugging-port=${Math.floor(Math.random() * (9999 - 9000) + 9000)}`,
],
});
@ -299,10 +276,7 @@ module.exports = grunt => {
.start()
.then(() =>
app.client.waitUntil(
() =>
app.client
.execute(getMochaResults)
.then(data => Boolean(data.value)),
() => app.client.execute(getMochaResults).then(data => Boolean(data.value)),
25000,
'Expected to find window.mochaResults set!'
)
@ -312,8 +286,7 @@ module.exports = grunt => {
const results = data.value;
if (results.failures > 0) {
console.error(results.reports);
failure = () =>
grunt.fail.fatal(`Found ${results.failures} failing unit tests.`);
failure = () => grunt.fail.fatal(`Found ${results.failures} failing unit tests.`);
return app.client.log('browser');
}
grunt.log.ok(`${results.passes} tests passed.`);
@ -327,10 +300,7 @@ module.exports = grunt => {
}
})
.catch(error => {
failure = () =>
grunt.fail.fatal(
`Something went wrong: ${error.message} ${error.stack}`
);
failure = () => grunt.fail.fatal(`Something went wrong: ${error.message} ${error.stack}`);
})
.then(() => {
// We need to use the failure variable and this early stop to clean up before
@ -371,16 +341,12 @@ module.exports = grunt => {
});
}
grunt.registerTask(
'unit-tests',
'Run unit tests w/Electron',
function thisNeeded() {
const environment = grunt.option('env') || 'test';
const done = this.async();
grunt.registerTask('unit-tests', 'Run unit tests w/Electron', function thisNeeded() {
const environment = grunt.option('env') || 'test';
const done = this.async();
runTests(environment, done);
}
);
runTests(environment, done);
});
grunt.registerTask(
'lib-unit-tests',
@ -393,117 +359,97 @@ module.exports = grunt => {
}
);
grunt.registerTask(
'loki-unit-tests',
'Run loki unit tests w/Electron',
function thisNeeded() {
const environment = grunt.option('env') || 'test-loki';
const done = this.async();
grunt.registerTask('loki-unit-tests', 'Run loki unit tests w/Electron', function thisNeeded() {
const environment = grunt.option('env') || 'test-loki';
const done = this.async();
runTests(environment, done);
}
);
runTests(environment, done);
});
grunt.registerMultiTask(
'test-release',
'Test packaged releases',
function thisNeeded() {
const dir = grunt.option('dir') || 'release';
const environment = grunt.option('env') || 'production';
const config = this.data;
const archive = [dir, config.archive].join('/');
const files = [
'config/default.json',
`config/${environment}.json`,
`config/local-${environment}.json`,
];
grunt.registerMultiTask('test-release', 'Test packaged releases', function thisNeeded() {
const dir = grunt.option('dir') || 'release';
const environment = grunt.option('env') || 'production';
const config = this.data;
const archive = [dir, config.archive].join('/');
const files = [
'config/default.json',
`config/${environment}.json`,
`config/local-${environment}.json`,
];
console.log(this.target, archive);
const releaseFiles = files.concat(config.files || []);
releaseFiles.forEach(fileName => {
console.log(fileName);
try {
asar.statFile(archive, fileName);
return true;
} catch (e) {
console.log(e);
throw new Error(`Missing file ${fileName}`);
}
});
console.log(this.target, archive);
const releaseFiles = files.concat(config.files || []);
releaseFiles.forEach(fileName => {
console.log(fileName);
try {
asar.statFile(archive, fileName);
return true;
} catch (e) {
console.log(e);
throw new Error(`Missing file ${fileName}`);
}
});
if (config.appUpdateYML) {
const appUpdateYML = [dir, config.appUpdateYML].join('/');
if (fs.existsSync(appUpdateYML)) {
console.log('auto update ok');
} else {
throw new Error(`Missing auto update config ${appUpdateYML}`);
}
if (config.appUpdateYML) {
const appUpdateYML = [dir, config.appUpdateYML].join('/');
if (fs.existsSync(appUpdateYML)) {
console.log('auto update ok');
} else {
throw new Error(`Missing auto update config ${appUpdateYML}`);
}
}
const done = this.async();
// A simple test to verify a visible window is opened with a title
const { Application } = spectron;
const done = this.async();
// A simple test to verify a visible window is opened with a title
const { Application } = spectron;
const app = new Application({
path: [dir, config.exe].join('/'),
requireName: 'unused',
chromeDriverArgs: [
`remote-debugging-port=${Math.floor(
Math.random() * (9999 - 9000) + 9000
)}`,
],
});
const app = new Application({
path: [dir, config.exe].join('/'),
requireName: 'unused',
chromeDriverArgs: [
`remote-debugging-port=${Math.floor(Math.random() * (9999 - 9000) + 9000)}`,
],
});
app
.start()
.then(() => app.client.getWindowCount())
.then(count => {
assert.equal(count, 1);
console.log('window opened');
})
.then(() =>
// Get the window's title
app.client.getTitle()
)
.then(title => {
// TODO: restore once fixed on win
if (this.target !== 'win') {
// Verify the window's title
assert.equal(title, packageJson.productName);
console.log('title ok');
}
})
.then(() => {
assert(
app.chromeDriver.logLines.indexOf(`NODE_ENV ${environment}`) > -1
);
console.log('environment ok');
})
.then(
() =>
// Successfully completed test
app.stop(),
error =>
// Test failed!
app.stop().then(() => {
grunt.fail.fatal(`Test failed: ${error.message} ${error.stack}`);
})
)
.then(done);
}
);
app
.start()
.then(() => app.client.getWindowCount())
.then(count => {
assert.equal(count, 1);
console.log('window opened');
})
.then(() =>
// Get the window's title
app.client.getTitle()
)
.then(title => {
// TODO: restore once fixed on win
if (this.target !== 'win') {
// Verify the window's title
assert.equal(title, packageJson.productName);
console.log('title ok');
}
})
.then(() => {
assert(app.chromeDriver.logLines.indexOf(`NODE_ENV ${environment}`) > -1);
console.log('environment ok');
})
.then(
() =>
// Successfully completed test
app.stop(),
error =>
// Test failed!
app.stop().then(() => {
grunt.fail.fatal(`Test failed: ${error.message} ${error.stack}`);
})
)
.then(done);
});
grunt.registerTask('tx', [
'exec:tx-pull-new',
'exec:tx-pull',
'locale-patch',
]);
grunt.registerTask('tx', ['exec:tx-pull-new', 'exec:tx-pull', 'locale-patch']);
grunt.registerTask('dev', ['default', 'watch']);
grunt.registerTask('test', [
'unit-tests',
'lib-unit-tests',
'loki-unit-tests',
]);
grunt.registerTask('test', ['unit-tests', 'lib-unit-tests', 'loki-unit-tests']);
grunt.registerTask('date', ['gitinfo', 'getExpireTime']);
grunt.registerTask('default', [
'exec:build-protobuf',

@ -8,8 +8,4 @@ interface Options {
allowMalformedOnStartup: boolean;
}
export function start(
name: string,
targetPath: string,
options: Options
): BaseConfig;
export function start(name: string, targetPath: string, options: Options): BaseConfig;

@ -18,9 +18,7 @@ function start(name, targetPath, options = {}) {
console.log(`config/get: Successfully read ${name} config file`);
if (!cachedValue) {
console.log(
`config/get: ${name} config value was falsy, cache is now empty object`
);
console.log(`config/get: ${name} config value was falsy, cache is now empty object`);
cachedValue = Object.create(null);
}
} catch (error) {
@ -28,9 +26,7 @@ function start(name, targetPath, options = {}) {
throw error;
}
console.log(
`config/get: Did not find ${name} config file, cache is now empty object`
);
console.log(`config/get: Did not find ${name} config file, cache is now empty object`);
cachedValue = Object.create(null);
}

@ -36,10 +36,8 @@ const config = require('config');
config.environment = environment;
// Log resulting env vars in use by config
['NODE_ENV', 'NODE_APP_INSTANCE', 'NODE_CONFIG_DIR', 'NODE_CONFIG'].forEach(
s => {
console.log(`${s} ${config.util.getEnv(s)}`);
}
);
['NODE_ENV', 'NODE_APP_INSTANCE', 'NODE_CONFIG_DIR', 'NODE_CONFIG'].forEach(s => {
console.log(`${s} ${config.util.getEnv(s)}`);
});
module.exports = config;

@ -13,13 +13,7 @@ function normalizeLocaleName(locale) {
function getLocaleMessages(locale) {
const onDiskLocale = locale.replace('-', '_');
const targetFile = path.join(
__dirname,
'..',
'_locales',
onDiskLocale,
'messages.json'
);
const targetFile = path.join(__dirname, '..', '_locales', onDiskLocale, 'messages.json');
return JSON.parse(fs.readFileSync(targetFile, 'utf-8'));
}
@ -49,9 +43,7 @@ function load({ appLocale, logger } = {}) {
// We start with english, then overwrite that with anything present in locale
messages = _.merge(english, messages);
} catch (e) {
logger.error(
`Problem loading messages for locale ${localeName} ${e.stack}`
);
logger.error(`Problem loading messages for locale ${localeName} ${e.stack}`);
logger.error('Falling back to en locale');
localeName = 'en';

@ -120,10 +120,7 @@ async function cleanupLogs(logPath) {
await eliminateOldEntries(files, earliestDate);
} catch (error) {
console.error(
'Error cleaning logs; deleting and starting over from scratch.',
error.stack
);
console.error('Error cleaning logs; deleting and starting over from scratch.', error.stack);
// delete and re-create the log directory
await deleteAllLogs(logPath);
@ -151,26 +148,24 @@ function eliminateOutOfDateFiles(logPath, date) {
return Promise.all(
_.map(paths, target =>
Promise.all([readFirstLine(target), readLastLines(target, 2)]).then(
results => {
const start = results[0];
const end = results[1].split('\n');
const file = {
path: target,
start: isLineAfterDate(start, date),
end:
isLineAfterDate(end[end.length - 1], date) ||
isLineAfterDate(end[end.length - 2], date),
};
if (!file.start && !file.end) {
fs.unlinkSync(file.path);
}
return file;
Promise.all([readFirstLine(target), readLastLines(target, 2)]).then(results => {
const start = results[0];
const end = results[1].split('\n');
const file = {
path: target,
start: isLineAfterDate(start, date),
end:
isLineAfterDate(end[end.length - 1], date) ||
isLineAfterDate(end[end.length - 2], date),
};
if (!file.start && !file.end) {
fs.unlinkSync(file.path);
}
)
return file;
})
)
);
}
@ -181,10 +176,7 @@ function eliminateOldEntries(files, date) {
return Promise.all(
_.map(files, file =>
fetchLog(file.path).then(lines => {
const recent = _.filter(
lines,
line => new Date(line.time).getTime() >= earliest
);
const recent = _.filter(lines, line => new Date(line.time).getTime() >= earliest);
const text = _.map(recent, line => JSON.stringify(line)).join('\n');
return fs.writeFileSync(file.path, `${text}\n`);

@ -39,9 +39,7 @@ function installPermissionsHandler({ session, userConfig }) {
// they've already been used successfully.
session.defaultSession.setPermissionRequestHandler(null);
session.defaultSession.setPermissionRequestHandler(
_createPermissionHandler(userConfig)
);
session.defaultSession.setPermissionRequestHandler(_createPermissionHandler(userConfig));
}
module.exports = {

@ -32,19 +32,13 @@ function _createFileHandler({ userDataPath, installPath, isWindows }) {
const properCasing = isWindows ? realPath.toLowerCase() : realPath;
if (!path.isAbsolute(realPath)) {
console.log(
`Warning: denying request to non-absolute path '${realPath}'`
);
console.log(`Warning: denying request to non-absolute path '${realPath}'`);
return callback();
}
if (
!properCasing.startsWith(
isWindows ? userDataPath.toLowerCase() : userDataPath
) &&
!properCasing.startsWith(
isWindows ? installPath.toLowerCase() : installPath
)
!properCasing.startsWith(isWindows ? userDataPath.toLowerCase() : userDataPath) &&
!properCasing.startsWith(isWindows ? installPath.toLowerCase() : installPath)
) {
console.log(
`Warning: denying request to path '${realPath}' (userDataPath: '${userDataPath}', installPath: '${installPath}')`
@ -58,12 +52,7 @@ function _createFileHandler({ userDataPath, installPath, isWindows }) {
};
}
function installFileHandler({
protocol,
userDataPath,
installPath,
isWindows,
}) {
function installFileHandler({ protocol, userDataPath, installPath, isWindows }) {
protocol.interceptFileProtocol(
'file',
_createFileHandler({ userDataPath, installPath, isWindows })

@ -7,15 +7,7 @@ const { redactAll } = require('../js/modules/privacy');
const { remove: removeUserConfig } = require('./user_config');
const pify = require('pify');
const {
map,
isString,
fromPairs,
forEach,
last,
isEmpty,
isObject,
} = require('lodash');
const { map, isString, fromPairs, forEach, last, isEmpty, isObject } = require('lodash');
// To get long stack traces
// https://github.com/mapbox/node-sqlite3/wiki/API#sqlite3verbose
@ -1113,11 +1105,7 @@ async function updateLokiSchema(instance) {
`Current loki schema version: ${lokiSchemaVersion};`,
`Most recent schema version: ${LOKI_SCHEMA_VERSIONS.length};`
);
for (
let index = 0, max = LOKI_SCHEMA_VERSIONS.length;
index < max;
index += 1
) {
for (let index = 0, max = LOKI_SCHEMA_VERSIONS.length; index < max; index += 1) {
const runSchemaUpdate = LOKI_SCHEMA_VERSIONS[index];
// Yes, we really want to do this asynchronously, in order
@ -1127,9 +1115,7 @@ async function updateLokiSchema(instance) {
}
async function getLokiSchemaVersion(instance) {
const result = await instance.get(
'SELECT MAX(version) as version FROM loki_schema;'
);
const result = await instance.get('SELECT MAX(version) as version FROM loki_schema;');
if (!result || !result.version) {
return 0;
}
@ -1219,10 +1205,7 @@ async function initialize({ configDir, key, messages, passwordAttempt }) {
}
console.log('Database startup error:', error.stack);
const buttonIndex = dialog.showMessageBox({
buttons: [
messages.copyErrorAndQuit.message,
messages.clearAllData.message,
],
buttons: [messages.copyErrorAndQuit.message, messages.clearAllData.message],
defaultId: 0,
detail: redactAll(error.stack),
message: messages.databaseError.message,
@ -1231,9 +1214,7 @@ async function initialize({ configDir, key, messages, passwordAttempt }) {
});
if (buttonIndex === 0) {
clipboard.writeText(
`Database startup error:\n\n${redactAll(error.stack)}`
);
clipboard.writeText(`Database startup error:\n\n${redactAll(error.stack)}`);
} else {
await close();
await removeDB();
@ -1389,12 +1370,9 @@ async function createOrUpdate(table, data, instance) {
}
async function getById(table, id, instance) {
const row = await (db || instance).get(
`SELECT * FROM ${table} WHERE id = $id;`,
{
$id: id,
}
);
const row = await (db || instance).get(`SELECT * FROM ${table} WHERE id = $id;`, {
$id: id,
});
if (!row) {
return null;
@ -1414,10 +1392,7 @@ async function removeById(table, id) {
}
// Our node interface doesn't seem to allow you to replace one single ? with an array
await db.run(
`DELETE FROM ${table} WHERE id IN ( ${id.map(() => '?').join(', ')} );`,
id
);
await db.run(`DELETE FROM ${table} WHERE id IN ( ${id.map(() => '?').join(', ')} );`, id);
}
async function removeAllFromTable(table) {
@ -1427,12 +1402,9 @@ async function removeAllFromTable(table) {
// Conversations
async function getSwarmNodesForPubkey(pubkey) {
const row = await db.get(
`SELECT * FROM ${NODES_FOR_PUBKEY_TABLE} WHERE pubkey = $pubkey;`,
{
$pubkey: pubkey,
}
);
const row = await db.get(`SELECT * FROM ${NODES_FOR_PUBKEY_TABLE} WHERE pubkey = $pubkey;`, {
$pubkey: pubkey,
});
if (!row) {
return [];
@ -1463,9 +1435,7 @@ async function getConversationCount() {
const row = await db.get(`SELECT count(*) from ${CONVERSATIONS_TABLE};`);
if (!row) {
throw new Error(
`getConversationCount: Unable to get count of ${CONVERSATIONS_TABLE}`
);
throw new Error(`getConversationCount: Unable to get count of ${CONVERSATIONS_TABLE}`);
}
return row['count(*)'];
@ -1563,9 +1533,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_TABLE} WHERE id IN ( ${id
.map(() => '?')
.join(', ')} );`,
`DELETE FROM ${CONVERSATIONS_TABLE} WHERE id IN ( ${id.map(() => '?').join(', ')} );`,
id
);
}
@ -1590,12 +1558,9 @@ async function savePublicServerToken(data) {
// open groups v1 only
async function getPublicServerTokenByServerUrl(serverUrl) {
const row = await db.get(
`SELECT * FROM ${SERVERS_TOKEN_TABLE} WHERE serverUrl = $serverUrl;`,
{
$serverUrl: serverUrl,
}
);
const row = await db.get(`SELECT * FROM ${SERVERS_TOKEN_TABLE} WHERE serverUrl = $serverUrl;`, {
$serverUrl: serverUrl,
});
if (!row) {
return null;
@ -1605,12 +1570,9 @@ async function getPublicServerTokenByServerUrl(serverUrl) {
}
async function getConversationById(id) {
const row = await db.get(
`SELECT * FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`,
{
$id: id,
}
);
const row = await db.get(`SELECT * FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`, {
$id: id,
});
if (!row) {
return null;
@ -1620,16 +1582,12 @@ async function getConversationById(id) {
}
async function getAllConversations() {
const rows = await db.all(
`SELECT json FROM ${CONVERSATIONS_TABLE} 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 getAllConversationIds() {
const rows = await db.all(
`SELECT id FROM ${CONVERSATIONS_TABLE} ORDER BY id ASC;`
);
const rows = await db.all(`SELECT id FROM ${CONVERSATIONS_TABLE} ORDER BY id ASC;`);
return map(rows, row => row.id);
}
@ -1715,11 +1673,7 @@ async function searchMessages(query, { limit } = {}) {
}));
}
async function searchMessagesInConversation(
query,
conversationId,
{ limit } = {}
) {
async function searchMessagesInConversation(query, conversationId, { limit } = {}) {
const rows = await db.all(
`SELECT
messages.json,
@ -1748,9 +1702,7 @@ async function getMessageCount() {
const row = await db.get(`SELECT count(*) from ${MESSAGES_TABLE};`);
if (!row) {
throw new Error(
`getMessageCount: Unable to get count of ${MESSAGES_TABLE}`
);
throw new Error(`getMessageCount: Unable to get count of ${MESSAGES_TABLE}`);
}
return row['count(*)'];
@ -1962,9 +1914,7 @@ async function removeMessage(id) {
// Our node interface doesn't seem to allow you to replace one single ? with an array
await db.run(
`DELETE FROM ${MESSAGES_TABLE} WHERE id IN ( ${id
.map(() => '?')
.join(', ')} );`,
`DELETE FROM ${MESSAGES_TABLE} WHERE id IN ( ${id.map(() => '?').join(', ')} );`,
id
);
}
@ -1975,9 +1925,7 @@ async function getMessageIdsFromServerIds(serverIds, conversationId) {
}
// Sanitize the input as we're going to use it directly in the query
const validIds = serverIds
.map(id => Number(id))
.filter(n => !Number.isNaN(n));
const validIds = serverIds.map(id => Number(id)).filter(n => !Number.isNaN(n));
/*
Sqlite3 doesn't have a good way to have `IN` query with another query.
@ -2009,16 +1957,12 @@ async function getMessageById(id) {
}
async function getAllMessages() {
const rows = await db.all(
`SELECT json FROM ${MESSAGES_TABLE} ORDER BY id ASC;`
);
const rows = await db.all(`SELECT json FROM ${MESSAGES_TABLE} ORDER BY id ASC;`);
return map(rows, row => jsonToObject(row.json));
}
async function getAllMessageIds() {
const rows = await db.all(
`SELECT id FROM ${MESSAGES_TABLE} ORDER BY id ASC;`
);
const rows = await db.all(`SELECT id FROM ${MESSAGES_TABLE} ORDER BY id ASC;`);
return map(rows, row => row.id);
}
@ -2115,13 +2059,10 @@ async function getMessagesBySentAt(sentAt) {
}
async function getLastHashBySnode(convoId, snode) {
const row = await db.get(
'SELECT * FROM lastHashes WHERE snode = $snode AND id = $id;',
{
$snode: snode,
$id: convoId,
}
);
const row = await db.get('SELECT * FROM lastHashes WHERE snode = $snode AND id = $id;', {
$snode: snode,
$id: convoId,
});
if (!row) {
return null;
@ -2132,9 +2073,7 @@ async function getLastHashBySnode(convoId, snode) {
async function getSeenMessagesByHashList(hashes) {
const rows = await db.all(
`SELECT * FROM seenMessages WHERE hash IN ( ${hashes
.map(() => '?')
.join(', ')} );`,
`SELECT * FROM seenMessages WHERE hash IN ( ${hashes.map(() => '?').join(', ')} );`,
hashes
);
@ -2224,13 +2163,7 @@ async function updateUnprocessedAttempts(id, attempts) {
});
}
async function updateUnprocessedWithData(id, data = {}) {
const {
source,
sourceDevice,
serverTimestamp,
decrypted,
senderIdentity,
} = data;
const { source, sourceDevice, serverTimestamp, decrypted, senderIdentity } = data;
await db.run(
`UPDATE unprocessed SET
@ -2270,9 +2203,7 @@ async function getUnprocessedCount() {
}
async function getAllUnprocessed() {
const rows = await db.all(
'SELECT * FROM unprocessed ORDER BY timestamp ASC;'
);
const rows = await db.all('SELECT * FROM unprocessed ORDER BY timestamp ASC;');
return rows;
}
@ -2288,10 +2219,7 @@ async function removeUnprocessed(id) {
}
// Our node interface doesn't seem to allow you to replace one single ? with an array
await db.run(
`DELETE FROM unprocessed WHERE id IN ( ${id.map(() => '?').join(', ')} );`,
id
);
await db.run(`DELETE FROM unprocessed WHERE id IN ( ${id.map(() => '?').join(', ')} );`, id);
}
async function removeAllUnprocessed() {
@ -2318,9 +2246,7 @@ async function getNextAttachmentDownloadJobs(limit, options = {}) {
async function saveAttachmentDownloadJob(job) {
const { id, pending, timestamp } = job;
if (!id) {
throw new Error(
'saveAttachmentDownloadJob: Provided job did not have a truthy id'
);
throw new Error('saveAttachmentDownloadJob: Provided job did not have a truthy id');
}
await db.run(
@ -2344,18 +2270,13 @@ async function saveAttachmentDownloadJob(job) {
);
}
async function setAttachmentDownloadJobPending(id, pending) {
await db.run(
'UPDATE attachment_downloads SET pending = $pending WHERE id = $id;',
{
$id: id,
$pending: pending,
}
);
await db.run('UPDATE attachment_downloads SET pending = $pending WHERE id = $id;', {
$id: id,
$pending: pending,
});
}
async function resetAttachmentDownloadPending() {
await db.run(
'UPDATE attachment_downloads SET pending = 0 WHERE pending != 0;'
);
await db.run('UPDATE attachment_downloads SET pending = 0 WHERE pending != 0;');
}
async function removeAttachmentDownloadJob(id) {
return removeById(ATTACHMENT_DOWNLOADS_TABLE, id);
@ -2400,10 +2321,7 @@ async function removeAllConversations() {
await removeAllFromTable(CONVERSATIONS_TABLE);
}
async function getMessagesWithVisualMediaAttachments(
conversationId,
{ limit }
) {
async function getMessagesWithVisualMediaAttachments(conversationId, { limit }) {
const rows = await db.all(
`SELECT json FROM ${MESSAGES_TABLE} WHERE
conversationId = $conversationId AND
@ -2507,9 +2425,7 @@ async function removeKnownAttachments(allAttachments) {
const chunkSize = 50;
const total = await getMessageCount();
console.log(
`removeKnownAttachments: About to iterate through ${total} messages`
);
console.log(`removeKnownAttachments: About to iterate through ${total} messages`);
let count = 0;
let complete = false;
@ -2626,28 +2542,19 @@ async function removePrefixFromGroupConversations(instance) {
);
// We have another conversation with the same future name.
// We decided to keep only the conversation with the higher number of messages
const countMessagesOld = await getMessagesCountByConversation(
instance,
oldId,
{ limit: Number.MAX_VALUE }
);
const countMessagesNew = await getMessagesCountByConversation(
instance,
newId,
{ limit: Number.MAX_VALUE }
);
const countMessagesOld = await getMessagesCountByConversation(instance, oldId, {
limit: Number.MAX_VALUE,
});
const countMessagesNew = await getMessagesCountByConversation(instance, newId, {
limit: Number.MAX_VALUE,
});
console.log(
`countMessagesOld: ${countMessagesOld}, countMessagesNew: ${countMessagesNew}`
);
console.log(`countMessagesOld: ${countMessagesOld}, countMessagesNew: ${countMessagesNew}`);
const deleteId = countMessagesOld > countMessagesNew ? newId : oldId;
await instance.run(
`DELETE FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`,
{
$id: deleteId,
}
);
await instance.run(`DELETE FROM ${CONVERSATIONS_TABLE} WHERE id = $id;`, {
$id: deleteId,
});
}
const morphedObject = {
@ -2703,8 +2610,7 @@ function remove05PrefixFromStringIfNeeded(str) {
async function updateExistingClosedGroupToClosedGroup(instance) {
// the migration is called only once, so all current groups not being open groups are v1 closed group.
const allClosedGroupV1 =
(await getAllClosedGroupConversations(instance)) || [];
const allClosedGroupV1 = (await getAllClosedGroupConversations(instance)) || [];
await Promise.all(
allClosedGroupV1.map(async groupV1 => {
@ -2751,9 +2657,7 @@ async function getAllEncryptionKeyPairsForGroup(groupPublicKey) {
}
async function getAllEncryptionKeyPairsForGroupRaw(groupPublicKey) {
const pubkeyAsString = groupPublicKey.key
? groupPublicKey.key
: groupPublicKey;
const pubkeyAsString = groupPublicKey.key ? groupPublicKey.key : groupPublicKey;
const rows = await db.all(
`SELECT * FROM ${CLOSED_GROUP_V2_KEY_PAIRS_TABLE} WHERE groupPublicKey = $groupPublicKey ORDER BY timestamp ASC;`,
{
@ -2772,11 +2676,7 @@ async function getLatestClosedGroupEncryptionKeyPair(groupPublicKey) {
return rows[rows.length - 1];
}
async function addClosedGroupEncryptionKeyPair(
groupPublicKey,
keypair,
instance
) {
async function addClosedGroupEncryptionKeyPair(groupPublicKey, keypair, instance) {
const timestamp = Date.now();
await (db || instance).run(
@ -2803,9 +2703,7 @@ async function isKeyPairAlreadySaved(
) {
const allKeyPairs = await getAllEncryptionKeyPairsForGroup(groupPublicKey);
return (allKeyPairs || []).some(
k =>
newKeyPairInHex.publicHex === k.publicHex &&
newKeyPairInHex.privateHex === k.privateHex
k => newKeyPairInHex.publicHex === k.publicHex && newKeyPairInHex.privateHex === k.privateHex
);
}
@ -2882,10 +2780,7 @@ async function saveV2OpenGroupRoom(opengroupsv2Room) {
}
async function removeV2OpenGroupRoom(conversationId) {
await db.run(
`DELETE FROM ${OPEN_GROUP_ROOMS_V2_TABLE} WHERE conversationId = $conversationId`,
{
$conversationId: conversationId,
}
);
await db.run(`DELETE FROM ${OPEN_GROUP_ROOMS_V2_TABLE} WHERE conversationId = $conversationId`, {
$conversationId: conversationId,
});
}

@ -24,18 +24,14 @@ function initialize() {
try {
const fn = sql[callName];
if (!fn) {
throw new Error(
`sql channel: ${callName} is not an available function`
);
throw new Error(`sql channel: ${callName} is not an available function`);
}
const result = await fn(...args);
event.sender.send(`${SQL_CHANNEL_KEY}-done`, jobId, null, result);
} catch (error) {
const errorForDisplay = error && error.stack ? error.stack : error;
console.log(
`sql channel error with call ${callName}: ${errorForDisplay}`
);
console.log(`sql channel error with call ${callName}: ${errorForDisplay}`);
// FIXME this line cause the test-integration to fail and we probably don't need it during test
if (!process.env.NODE_ENV.includes('test-integration')) {
event.sender.send(`${SQL_CHANNEL_KEY}-done`, jobId, errorForDisplay);

@ -9,12 +9,7 @@ let tray = null;
function createTrayIcon(getMainWindow, messages) {
// A smaller icon is needed on macOS
const iconSize = process.platform === 'darwin' ? '16' : '256';
const iconNoNewMessages = path.join(
__dirname,
'..',
'images',
`icon_${iconSize}.png`
);
const iconNoNewMessages = path.join(__dirname, '..', 'images', `icon_${iconSize}.png`);
tray = new Tray(iconNoNewMessages);
@ -65,8 +60,7 @@ function createTrayIcon(getMainWindow, messages) {
trayContextMenu = Menu.buildFromTemplate([
{
id: 'toggleWindowVisibility',
label:
messages[mainWindow.isVisible() ? 'appMenuHide' : 'show'].message,
label: messages[mainWindow.isVisible() ? 'appMenuHide' : 'show'].message,
click: tray.toggleWindowVisibility,
},
{

@ -27,10 +27,7 @@ if (config.has(storageProfile)) {
}
if (storageProfile) {
const userData = path.join(
app.getPath('appData'),
`Session-${storageProfile}`
);
const userData = path.join(app.getPath('appData'), `Session-${storageProfile}`);
app.setPath('userData', userData);
}

@ -19,15 +19,8 @@ module.exports = async function(context) {
const executableName = context.packager.executableName;
const sourceExecutable = path.join(context.appOutDir, executableName);
const targetExecutable = path.join(
context.appOutDir,
`${executableName}-bin`
);
const launcherScript = path.join(
context.appOutDir,
'resources',
'launcher-script.sh'
);
const targetExecutable = path.join(context.appOutDir, `${executableName}-bin`);
const launcherScript = path.join(context.appOutDir, 'resources', 'launcher-script.sh');
const chromeSandbox = path.join(context.appOutDir, 'chrome-sandbox');
return Promise.all([

@ -21,16 +21,10 @@ exports.default = async function notarizing(context) {
log('Notarizing mac application');
const appName = context.packager.appInfo.productFilename;
const {
SIGNING_APPLE_ID,
SIGNING_APP_PASSWORD,
SIGNING_TEAM_ID,
} = process.env;
const { SIGNING_APPLE_ID, SIGNING_APP_PASSWORD, SIGNING_TEAM_ID } = process.env;
if (isEmpty(SIGNING_APPLE_ID) || isEmpty(SIGNING_APP_PASSWORD)) {
log(
'SIGNING_APPLE_ID or SIGNING_APP_PASSWORD not set.\nTerminating noratization.'
);
log('SIGNING_APPLE_ID or SIGNING_APP_PASSWORD not set.\nTerminating noratization.');
return;
}

@ -168,10 +168,7 @@
// Ensure accounts created prior to 1.0.0-beta8 do have their
// 'primaryDevicePubKey' defined.
if (
Whisper.Registration.isDone() &&
!storage.get('primaryDevicePubKey', null)
) {
if (Whisper.Registration.isDone() && !storage.get('primaryDevicePubKey', null)) {
storage.put(
'primaryDevicePubKey',
window.libsession.Utils.UserUtils.getOurPubKeyStrFromCache()
@ -226,9 +223,7 @@
await storage.put('version', currentVersion);
if (newVersion) {
window.log.info(
`New version detected: ${currentVersion}; previous: ${lastVersion}`
);
window.log.info(`New version detected: ${currentVersion}; previous: ${lastVersion}`);
await window.Signal.Data.cleanupOrphanedAttachments();
@ -265,31 +260,26 @@
}
});
Whisper.events.on(
'deleteLocalPublicMessages',
async ({ messageServerIds, conversationId }) => {
if (!Array.isArray(messageServerIds)) {
return;
}
const messageIds = await window.Signal.Data.getMessageIdsFromServerIds(
messageServerIds,
conversationId
);
if (messageIds.length === 0) {
return;
}
const conversation = window
.getConversationController()
.get(conversationId);
messageIds.forEach(id => {
if (conversation) {
conversation.removeMessage(id);
}
window.Signal.Data.removeMessage(id);
});
Whisper.events.on('deleteLocalPublicMessages', async ({ messageServerIds, conversationId }) => {
if (!Array.isArray(messageServerIds)) {
return;
}
const messageIds = await window.Signal.Data.getMessageIdsFromServerIds(
messageServerIds,
conversationId
);
if (messageIds.length === 0) {
return;
}
);
const conversation = window.getConversationController().get(conversationId);
messageIds.forEach(id => {
if (conversation) {
conversation.removeMessage(id);
}
window.Signal.Data.removeMessage(id);
});
});
function manageExpiringData() {
window.Signal.Data.cleanSeenMessages();
@ -303,9 +293,7 @@
window.log.info('Cleanup: starting...');
const results = await Promise.all([
window.Signal.Data.getOutgoingWithoutExpiresAt(),
]);
const results = await Promise.all([window.Signal.Data.getOutgoingWithoutExpiresAt()]);
// Combine the models
const messagesForCleanup = results.reduce(
@ -313,29 +301,20 @@
[]
);
window.log.info(
`Cleanup: Found ${messagesForCleanup.length} messages for cleanup`
);
window.log.info(`Cleanup: Found ${messagesForCleanup.length} messages for cleanup`);
await Promise.all(
messagesForCleanup.map(async message => {
const delivered = message.get('delivered');
const sentAt = message.get('sent_at');
const expirationStartTimestamp = message.get(
'expirationStartTimestamp'
);
const expirationStartTimestamp = message.get('expirationStartTimestamp');
if (message.hasErrors()) {
return;
}
if (delivered) {
window.log.info(
`Cleanup: Starting timer for delivered message ${sentAt}`
);
message.set(
'expirationStartTimestamp',
expirationStartTimestamp || sentAt
);
window.log.info(`Cleanup: Starting timer for delivered message ${sentAt}`);
message.set('expirationStartTimestamp', expirationStartTimestamp || sentAt);
await message.setToExpire();
return;
}
@ -485,13 +464,11 @@
profileKey
);
const avatarPointer = await libsession.Utils.AttachmentUtils.uploadAvatar(
{
...dataResized,
data: encryptedData,
size: encryptedData.byteLength,
}
);
const avatarPointer = await libsession.Utils.AttachmentUtils.uploadAvatar({
...dataResized,
data: encryptedData,
size: encryptedData.byteLength,
});
({ url } = avatarPointer);
@ -512,12 +489,8 @@
avatar: newAvatarPath,
});
await conversation.commit();
window.libsession.Utils.UserUtils.setLastProfileUpdateTimestamp(
Date.now()
);
await window.libsession.Utils.SyncUtils.forceSyncConfigurationNowIfNeeded(
true
);
window.libsession.Utils.UserUtils.setLastProfileUpdateTimestamp(Date.now());
await window.libsession.Utils.SyncUtils.forceSyncConfigurationNowIfNeeded(true);
} catch (error) {
window.log.error(
'showEditProfileDialog Error ensuring that image is properly sized:',
@ -531,12 +504,8 @@
});
// might be good to not trigger a sync if the name did not change
await conversation.commit();
window.libsession.Utils.UserUtils.setLastProfileUpdateTimestamp(
Date.now()
);
await window.libsession.Utils.SyncUtils.forceSyncConfigurationNowIfNeeded(
true
);
window.libsession.Utils.UserUtils.setLastProfileUpdateTimestamp(Date.now());
await window.libsession.Utils.SyncUtils.forceSyncConfigurationNowIfNeeded(true);
}
// inform all your registered public servers
@ -551,9 +520,7 @@
.getConversationController()
.getConversations()
.filter(convo => convo.isPublic())
.forEach(convo =>
convo.trigger('ourAvatarChanged', { url, profileKey })
);
.forEach(convo => convo.trigger('ourAvatarChanged', { url, profileKey }));
}
},
});
@ -640,46 +607,37 @@
}
});
Whisper.events.on(
'publicChatInvitationAccepted',
async (serverAddress, channelId) => {
// To some degree this has been copy-pasted
// form connection_to_server_dialog_view.js:
const rawServerUrl = serverAddress
.replace(/^https?:\/\//i, '')
.replace(/[/\\]+$/i, '');
const sslServerUrl = `https://${rawServerUrl}`;
const conversationId = `publicChat:${channelId}@${rawServerUrl}`;
const conversationExists = window
.getConversationController()
.get(conversationId);
if (conversationExists) {
window.log.warn('We are already a member of this public chat');
window.libsession.Utils.ToastUtils.pushAlreadyMemberOpenGroup();
Whisper.events.on('publicChatInvitationAccepted', async (serverAddress, channelId) => {
// To some degree this has been copy-pasted
// form connection_to_server_dialog_view.js:
const rawServerUrl = serverAddress.replace(/^https?:\/\//i, '').replace(/[/\\]+$/i, '');
const sslServerUrl = `https://${rawServerUrl}`;
const conversationId = `publicChat:${channelId}@${rawServerUrl}`;
return;
}
const conversationExists = window.getConversationController().get(conversationId);
if (conversationExists) {
window.log.warn('We are already a member of this public chat');
window.libsession.Utils.ToastUtils.pushAlreadyMemberOpenGroup();
const conversation = await window
.getConversationController()
.getOrCreateAndWait(conversationId, 'group');
await conversation.setPublicSource(sslServerUrl, channelId);
const channelAPI = await window.lokiPublicChatAPI.findOrCreateChannel(
sslServerUrl,
channelId,
conversationId
);
if (!channelAPI) {
window.log.warn(`Could not connect to ${serverAddress}`);
return;
}
window.inboxStore.dispatch(
window.actionsCreators.openConversationExternal(conversationId)
);
return;
}
);
const conversation = await window
.getConversationController()
.getOrCreateAndWait(conversationId, 'group');
await conversation.setPublicSource(sslServerUrl, channelId);
const channelAPI = await window.lokiPublicChatAPI.findOrCreateChannel(
sslServerUrl,
channelId,
conversationId
);
if (!channelAPI) {
window.log.warn(`Could not connect to ${serverAddress}`);
return;
}
window.inboxStore.dispatch(window.actionsCreators.openConversationExternal(conversationId));
});
Whisper.events.on('leaveGroup', async groupConvo => {
if (appView) {
@ -690,9 +648,7 @@
Whisper.Notifications.on('click', (id, messageId) => {
window.showWindow();
if (id) {
window.inboxStore.dispatch(
window.actionsCreators.openConversationExternal(id, messageId)
);
window.inboxStore.dispatch(window.actionsCreators.openConversationExternal(id, messageId));
} else {
appView.openInbox({
initialLoadComplete,
@ -802,9 +758,7 @@
window.addEventListener('offline', onOffline);
}
if (connectCount === 0 && !navigator.onLine) {
window.log.warn(
'Starting up offline; will connect when we have network access'
);
window.log.warn('Starting up offline; will connect when we have network access');
window.addEventListener('online', onOnline);
onEmpty(); // this ensures that the loading screen is dismissed
return;
@ -841,14 +795,8 @@
initAPIs();
await initSpecialConversations();
messageReceiver = new textsecure.MessageReceiver();
messageReceiver.addEventListener(
'message',
window.DataMessageReceiver.handleMessageEvent
);
messageReceiver.addEventListener(
'sent',
window.DataMessageReceiver.handleMessageEvent
);
messageReceiver.addEventListener('message', window.DataMessageReceiver.handleMessageEvent);
messageReceiver.addEventListener('sent', window.DataMessageReceiver.handleMessageEvent);
messageReceiver.addEventListener('reconnect', onReconnect);
messageReceiver.addEventListener('configuration', onConfiguration);
// messageReceiver.addEventListener('typing', onTyping);

@ -64,11 +64,7 @@
};
request.onerror = () => {
Whisper.Database.handleDOMException(
'clearStores request error',
request.error,
reject
);
Whisper.Database.handleDOMException('clearStores request error', request.error, reject);
};
});
});

@ -35,23 +35,19 @@
}
const message = messages.find(
item =>
!item.isIncoming() && originalSource === item.get('conversationId')
item => !item.isIncoming() && originalSource === item.get('conversationId')
);
if (message) {
return message;
}
const groups = await window.Signal.Data.getAllGroupsInvolvingId(
originalSource
);
const groups = await window.Signal.Data.getAllGroupsInvolvingId(originalSource);
const ids = groups.pluck('id');
ids.push(originalSource);
const target = messages.find(
item =>
!item.isIncoming() && _.contains(ids, item.get('conversationId'))
item => !item.isIncoming() && _.contains(ids, item.get('conversationId'))
);
if (!target) {
return null;
@ -61,14 +57,9 @@
},
async onReceipt(receipt) {
try {
const messages = await window.Signal.Data.getMessagesBySentAt(
receipt.get('timestamp')
);
const messages = await window.Signal.Data.getMessagesBySentAt(receipt.get('timestamp'));
const message = await this.getTargetMessage(
receipt.get('source'),
messages
);
const message = await this.getTargetMessage(receipt.get('source'), messages);
if (!message) {
window.log.info(
'No message for delivery receipt',
@ -80,9 +71,7 @@
const deliveries = message.get('delivered') || 0;
const deliveredTo = message.get('delivered_to') || [];
const expirationStartTimestamp = message.get(
'expirationStartTimestamp'
);
const expirationStartTimestamp = message.get('expirationStartTimestamp');
message.set({
delivered_to: _.union(deliveredTo, [receipt.get('source')]),
delivered: deliveries + 1,
@ -98,9 +87,7 @@
}
// notify frontend listeners
const conversation = window
.getConversationController()
.get(message.get('conversationId'));
const conversation = window.getConversationController().get(message.get('conversationId'));
if (conversation) {
conversation.updateLastMessage();
}

@ -21,9 +21,7 @@
window.libsession.Utils.UserUtils.getOurPubKeyStrFromCache();
} catch (e) {
// give it a minute
log.warn(
'Could not check to see if newer version is available cause our pubkey is not set'
);
log.warn('Could not check to see if newer version is available cause our pubkey is not set');
nextWaitSeconds = 60;
setTimeout(async () => {
await checkForUpgrades();
@ -81,9 +79,7 @@
if (expiredVersion !== null) {
return res(expiredVersion);
}
log.info(
`Delaying sending checks for ${nextWaitSeconds}s, no version yet`
);
log.info(`Delaying sending checks for ${nextWaitSeconds}s, no version yet`);
setTimeout(waitForVersion, nextWaitSeconds * 1000);
return true;
}
@ -112,9 +108,7 @@
let timestamp = NaN;
try {
const res = await window.tokenlessFileServerAdnAPI.serverRequest(
'loki/v1/time'
);
const res = await window.tokenlessFileServerAdnAPI.serverRequest('loki/v1/time');
if (res.ok) {
timestamp = res.response;
}

@ -81,10 +81,7 @@
clearTimeout(timeout);
timeout = setTimeout(destroyExpiredMessages, wait);
}
const throttledCheckExpiringMessages = _.throttle(
checkExpiringMessages,
1000
);
const throttledCheckExpiringMessages = _.throttle(checkExpiringMessages, 1000);
Whisper.ExpiringMessagesListener = {
nextExpiration: null,
@ -103,11 +100,7 @@
);
},
getAbbreviated() {
return i18n(
['timerOption', this.get('time'), this.get('unit'), 'abbreviated'].join(
'_'
)
);
return i18n(['timerOption', this.get('time'), this.get('unit'), 'abbreviated'].join('_'));
},
});
Whisper.ExpirationTimerOptions = new (Backbone.Collection.extend({

@ -31,10 +31,7 @@
throw new Error('Tried to store undefined');
}
if (!ready) {
window.log.warn(
'Called storage.put before storage is ready. key:',
key
);
window.log.warn('Called storage.put before storage is ready. key:', key);
}
const item = items.add({ id: key, value }, { merge: true });
return new Promise((resolve, reject) => {

@ -136,9 +136,7 @@ async function _runJob(job) {
try {
if (!job || !attachment || !messageId) {
throw new Error(
`_runJob: Key information required for job was missing. Job id: ${id}`
);
throw new Error(`_runJob: Key information required for job was missing. Job id: ${id}`);
}
const found = await getMessageById(messageId);
@ -166,20 +164,14 @@ async function _runJob(job) {
);
await _finishJob(message, id);
await _addAttachmentToMessage(
message,
_markAttachmentAsError(attachment),
{ type, index }
);
await _addAttachmentToMessage(message, _markAttachmentAsError(attachment), { type, index });
return;
}
throw error;
}
const upgradedAttachment = await Signal.Migrations.processNewAttachment(
downloaded
);
const upgradedAttachment = await Signal.Migrations.processNewAttachment(downloaded);
await _addAttachmentToMessage(message, upgradedAttachment, { type, index });
@ -194,11 +186,7 @@ async function _runJob(job) {
);
await _finishJob(message, id);
await _addAttachmentToMessage(
message,
_markAttachmentAsError(attachment),
{ type, index }
);
await _addAttachmentToMessage(message, _markAttachmentAsError(attachment), { type, index });
return;
}
@ -267,9 +255,7 @@ async function _addAttachmentToMessage(message, attachment, { type, index }) {
if (type === 'preview') {
const preview = message.get('preview');
if (!preview || preview.length <= index) {
throw new Error(
`_addAttachmentToMessage: preview didn't exist or ${index} was too large`
);
throw new Error(`_addAttachmentToMessage: preview didn't exist or ${index} was too large`);
}
const item = preview[index];
if (!item) {
@ -282,9 +268,7 @@ async function _addAttachmentToMessage(message, attachment, { type, index }) {
if (type === 'contact') {
const contact = message.get('contact');
if (!contact || contact.length <= index) {
throw new Error(
`_addAttachmentToMessage: contact didn't exist or ${index} was too large`
);
throw new Error(`_addAttachmentToMessage: contact didn't exist or ${index} was too large`);
}
const item = contact[index];
if (item && item.avatar && item.avatar.avatar) {
@ -312,9 +296,7 @@ async function _addAttachmentToMessage(message, attachment, { type, index }) {
const item = attachments[index];
if (!item) {
throw new Error(
`_addAttachmentToMessage: attachment ${index} was falsey`
);
throw new Error(`_addAttachmentToMessage: attachment ${index} was falsey`);
}
_replaceAttachment(item, 'thumbnail', attachment, logPrefix);
return;

@ -31,10 +31,7 @@ exports.autoOrientImage = (fileOrBlobOrURL, options = {}) => {
}
const canvas = canvasOrError;
const dataURL = canvas.toDataURL(
optionsWithDefaults.type,
optionsWithDefaults.quality
);
const dataURL = canvas.toDataURL(optionsWithDefaults.type, optionsWithDefaults.quality);
resolve(dataURL);
},

@ -174,8 +174,7 @@ async function importConversationsFromJSON(conversations, options) {
for (let i = 0, max = conversations.length; i < max; i += 1) {
const toAdd = unstringify(conversations[i]);
const haveConversationAlready =
conversationLookup[getConversationKey(toAdd)];
const haveConversationAlready = conversationLookup[getConversationKey(toAdd)];
if (haveConversationAlready) {
skipCount += 1;
@ -186,23 +185,14 @@ async function importConversationsFromJSON(conversations, options) {
count += 1;
// eslint-disable-next-line no-await-in-loop
const migrated = await window.Signal.Types.Conversation.migrateConversation(
toAdd,
{
writeNewAttachmentData,
}
);
const migrated = await window.Signal.Types.Conversation.migrateConversation(toAdd, {
writeNewAttachmentData,
});
// eslint-disable-next-line no-await-in-loop
await window.Signal.Data.saveConversation(migrated);
}
window.log.info(
'Done importing conversations:',
'Total count:',
count,
'Skipped:',
skipCount
);
window.log.info('Done importing conversations:', 'Total count:', count, 'Skipped:', skipCount);
}
async function importFromJsonString(jsonString, targetPath, options) {
@ -229,9 +219,7 @@ async function importFromJsonString(jsonString, targetPath, options) {
delete importObject.sessions;
delete importObject.unprocessed;
window.log.info(
'This is a light import; contacts, groups and messages only'
);
window.log.info('This is a light import; contacts, groups and messages only');
}
// We mutate the on-disk backup to prevent the user from importing client
@ -260,9 +248,7 @@ async function importFromJsonString(jsonString, targetPath, options) {
_.map(remainingStoreNames, async storeName => {
const save = SAVE_FUNCTIONS[storeName];
if (!_.isFunction(save)) {
throw new Error(
`importFromJsonString: Didn't have save function for store ${storeName}`
);
throw new Error(`importFromJsonString: Didn't have save function for store ${storeName}`);
}
window.log.info(`Importing items for store ${storeName}`);
@ -279,12 +265,7 @@ async function importFromJsonString(jsonString, targetPath, options) {
await save(toAdd);
}
window.log.info(
'Done importing to store',
storeName,
'Total count:',
toImport.length
);
window.log.info('Done importing to store', storeName, 'Total count:', toImport.length);
})
);
@ -339,10 +320,7 @@ function readFileAsText(parent, name) {
// Buffer instances are also Uint8Array instances, but they might be a view
// https://nodejs.org/docs/latest/api/buffer.html#buffer_buffers_and_typedarray
const toArrayBuffer = nodeBuffer =>
nodeBuffer.buffer.slice(
nodeBuffer.byteOffset,
nodeBuffer.byteOffset + nodeBuffer.byteLength
);
nodeBuffer.buffer.slice(nodeBuffer.byteOffset, nodeBuffer.byteOffset + nodeBuffer.byteLength);
function readFileAsArrayBuffer(targetPath) {
return new Promise((resolve, reject) => {
@ -381,9 +359,7 @@ function _getExportAttachmentFileName(message, index, attachment) {
if (attachment.contentType) {
const components = attachment.contentType.split('/');
name += `.${
components.length > 1 ? components[1] : attachment.contentType
}`;
name += `.${components.length > 1 ? components[1] : attachment.contentType}`;
}
return name;
@ -413,11 +389,7 @@ async function readEncryptedAttachment(dir, attachment, name, options) {
const isEncrypted = !_.isUndefined(key);
if (isEncrypted) {
attachment.data = await crypto.decryptAttachment(
key,
attachment.path,
data
);
attachment.data = await crypto.decryptAttachment(key, attachment.path, data);
} else {
attachment.data = data;
}
@ -429,10 +401,7 @@ async function writeQuoteThumbnail(attachment, options) {
}
const { dir, message, index, key, newKey } = options;
const filename = `${_getAnonymousAttachmentFileName(
message,
index
)}-quote-thumbnail`;
const filename = `${_getAnonymousAttachmentFileName(message, index)}-quote-thumbnail`;
const target = path.join(dir, filename);
await writeEncryptedAttachment(target, attachment.thumbnail.path, {
@ -485,10 +454,7 @@ async function writeAttachment(attachment, options) {
});
if (attachment.thumbnail && _.isString(attachment.thumbnail.path)) {
const thumbnailName = `${_getAnonymousAttachmentFileName(
message,
index
)}-thumbnail`;
const thumbnailName = `${_getAnonymousAttachmentFileName(message, index)}-thumbnail`;
const thumbnailTarget = path.join(dir, thumbnailName);
await writeEncryptedAttachment(thumbnailTarget, attachment.thumbnail.path, {
key,
@ -499,21 +465,14 @@ async function writeAttachment(attachment, options) {
}
if (attachment.screenshot && _.isString(attachment.screenshot.path)) {
const screenshotName = `${_getAnonymousAttachmentFileName(
message,
index
)}-screenshot`;
const screenshotName = `${_getAnonymousAttachmentFileName(message, index)}-screenshot`;
const screenshotTarget = path.join(dir, screenshotName);
await writeEncryptedAttachment(
screenshotTarget,
attachment.screenshot.path,
{
key,
newKey,
filename: screenshotName,
dir,
}
);
await writeEncryptedAttachment(screenshotTarget, attachment.screenshot.path, {
key,
newKey,
filename: screenshotName,
dir,
});
}
}
@ -686,13 +645,10 @@ async function exportConversation(conversation, options = {}) {
while (!complete) {
// eslint-disable-next-line no-await-in-loop
const collection = await window.Signal.Data.getMessagesByConversation(
conversation.id,
{
limit: CHUNK_SIZE,
receivedAt: lastReceivedAt,
}
);
const collection = await window.Signal.Data.getMessagesByConversation(conversation.id, {
limit: CHUNK_SIZE,
receivedAt: lastReceivedAt,
});
const messages = getPlainJS(collection);
for (let i = 0, max = messages.length; i < max; i += 1) {
@ -712,9 +668,7 @@ async function exportConversation(conversation, options = {}) {
const { attachments } = message;
// eliminate attachment data from the JSON, since it will go to disk
// Note: this is for legacy messages only, which stored attachment data in the db
message.attachments = _.map(attachments, attachment =>
_.omit(attachment, ['data'])
);
message.attachments = _.map(attachments, attachment => _.omit(attachment, ['data']));
// completely drop any attachments in messages cached in error objects
// TODO: move to lodash. Sadly, a number of the method signatures have changed!
message.errors = _.map(message.errors, error => {
@ -901,22 +855,12 @@ async function loadAttachments(dir, getName, options) {
if (attachment.thumbnail && _.isString(attachment.thumbnail.path)) {
const thumbnailName = `${name}-thumbnail`;
await readEncryptedAttachment(
dir,
attachment.thumbnail,
thumbnailName,
options
);
await readEncryptedAttachment(dir, attachment.thumbnail, thumbnailName, options);
}
if (attachment.screenshot && _.isString(attachment.screenshot.path)) {
const screenshotName = `${name}-screenshot`;
await readEncryptedAttachment(
dir,
attachment.screenshot,
screenshotName,
options
);
await readEncryptedAttachment(dir, attachment.screenshot, screenshotName, options);
}
})
);
@ -989,10 +933,7 @@ async function saveAllMessages(rawMessages) {
`[REDACTED]${conversationId.slice(-3)}`
);
} catch (error) {
window.log.error(
'saveAllMessages error',
error && error.message ? error.message : error
);
window.log.error('saveAllMessages error', error && error.message ? error.message : error);
}
}
@ -1015,18 +956,14 @@ async function importConversation(dir, options) {
try {
contents = await readFileAsText(dir, 'messages.json');
} catch (error) {
window.log.error(
`Warning: could not access messages.json in directory: ${dir}`
);
window.log.error(`Warning: could not access messages.json in directory: ${dir}`);
}
let promiseChain = Promise.resolve();
const json = JSON.parse(contents);
if (json.messages && json.messages.length) {
conversationId = `[REDACTED]${(json.messages[0].conversationId || '').slice(
-3
)}`;
conversationId = `[REDACTED]${(json.messages[0].conversationId || '').slice(-3)}`;
}
total = json.messages.length;
@ -1040,9 +977,7 @@ async function importConversation(dir, options) {
const hasAttachments = message.attachments && message.attachments.length;
const hasQuotedAttachments =
message.quote &&
message.quote.attachments &&
message.quote.attachments.length > 0;
message.quote && message.quote.attachments && message.quote.attachments.length > 0;
const hasContacts = message.contact && message.contact.length;
const hasPreviews = message.preview && message.preview.length;
@ -1051,8 +986,7 @@ async function importConversation(dir, options) {
const getName = attachmentsDir
? _getAnonymousAttachmentFileName
: _getExportAttachmentFileName;
const parentDir =
attachmentsDir || path.join(dir, message.received_at.toString());
const parentDir = attachmentsDir || path.join(dir, message.received_at.toString());
await loadAttachments(parentDir, getName, {
message,
@ -1229,10 +1163,7 @@ async function exportToDirectory(directory, options) {
window.log.info('done backing up!');
return directory;
} catch (error) {
window.log.error(
'The backup went wrong!',
error && error.stack ? error.stack : error
);
window.log.error('The backup went wrong!', error && error.stack ? error.stack : error);
throw error;
} finally {
if (stagingDir) {
@ -1255,10 +1186,7 @@ async function importFromDirectory(directory, options) {
options = options || {};
try {
const lookups = await Promise.all([
loadMessagesLookup(),
loadConversationLookup(),
]);
const lookups = await Promise.all([loadMessagesLookup(), loadConversationLookup()]);
const [messageLookup, conversationLookup] = lookups;
options = Object.assign({}, options, {
messageLookup,
@ -1274,9 +1202,7 @@ async function importFromDirectory(directory, options) {
// we're in the world of an encrypted, zipped backup
if (!options.key) {
throw new Error(
'Importing an encrypted backup; decryption key is required!'
);
throw new Error('Importing an encrypted backup; decryption key is required!');
}
let stagingDir;
@ -1315,10 +1241,7 @@ async function importFromDirectory(directory, options) {
window.log.info('Done importing!');
return result;
} catch (error) {
window.log.error(
'The import went wrong!',
error && error.stack ? error.stack : error
);
window.log.error('The import went wrong!', error && error.stack ? error.stack : error);
throw error;
}
}

@ -129,11 +129,7 @@ async function encryptSymmetric(key, plaintext) {
const cipherKey = await hmacSha256(key, nonce);
const macKey = await hmacSha256(key, cipherKey);
const cipherText = await _encrypt_aes256_CBC_PKCSPadding(
cipherKey,
iv,
plaintext
);
const cipherText = await _encrypt_aes256_CBC_PKCSPadding(cipherKey, iv, plaintext);
const mac = _getFirstBytes(await hmacSha256(macKey, cipherText), MAC_LENGTH);
return concatenateBytes(nonce, cipherText, mac);
@ -143,24 +139,15 @@ async function decryptSymmetric(key, data) {
const iv = getZeroes(IV_LENGTH);
const nonce = _getFirstBytes(data, NONCE_LENGTH);
const cipherText = _getBytes(
data,
NONCE_LENGTH,
data.byteLength - NONCE_LENGTH - MAC_LENGTH
);
const cipherText = _getBytes(data, NONCE_LENGTH, data.byteLength - NONCE_LENGTH - MAC_LENGTH);
const theirMac = _getBytes(data, data.byteLength - MAC_LENGTH, MAC_LENGTH);
const cipherKey = await hmacSha256(key, nonce);
const macKey = await hmacSha256(key, cipherKey);
const ourMac = _getFirstBytes(
await hmacSha256(macKey, cipherText),
MAC_LENGTH
);
const ourMac = _getFirstBytes(await hmacSha256(macKey, cipherText), MAC_LENGTH);
if (!constantTimeEqual(theirMac, ourMac)) {
throw new Error(
'decryptSymmetric: Failed to decrypt; MAC verification failed'
);
throw new Error('decryptSymmetric: Failed to decrypt; MAC verification failed');
}
return _decrypt_aes256_CBC_PKCSPadding(cipherKey, iv, cipherText);
@ -189,13 +176,9 @@ async function hmacSha256(key, plaintext) {
};
const extractable = false;
const cryptoKey = await window.crypto.subtle.importKey(
'raw',
key,
algorithm,
extractable,
['sign']
);
const cryptoKey = await window.crypto.subtle.importKey('raw', key, algorithm, extractable, [
'sign',
]);
return window.crypto.subtle.sign(algorithm, cryptoKey, plaintext);
}
@ -207,13 +190,9 @@ async function _encrypt_aes256_CBC_PKCSPadding(key, iv, plaintext) {
};
const extractable = false;
const cryptoKey = await window.crypto.subtle.importKey(
'raw',
key,
algorithm,
extractable,
['encrypt']
);
const cryptoKey = await window.crypto.subtle.importKey('raw', key, algorithm, extractable, [
'encrypt',
]);
return window.crypto.subtle.encrypt(algorithm, cryptoKey, plaintext);
}
@ -225,13 +204,9 @@ async function _decrypt_aes256_CBC_PKCSPadding(key, iv, plaintext) {
};
const extractable = false;
const cryptoKey = await window.crypto.subtle.importKey(
'raw',
key,
algorithm,
extractable,
['decrypt']
);
const cryptoKey = await window.crypto.subtle.importKey('raw', key, algorithm, extractable, [
'decrypt',
]);
return window.crypto.subtle.decrypt(algorithm, cryptoKey, plaintext);
}
@ -243,19 +218,9 @@ async function encryptAesCtr(key, plaintext, counter) {
length: 128,
};
const cryptoKey = await crypto.subtle.importKey(
'raw',
key,
algorithm,
extractable,
['encrypt']
);
const cryptoKey = await crypto.subtle.importKey('raw', key, algorithm, extractable, ['encrypt']);
const ciphertext = await crypto.subtle.encrypt(
algorithm,
cryptoKey,
plaintext
);
const ciphertext = await crypto.subtle.encrypt(algorithm, cryptoKey, plaintext);
return ciphertext;
}
@ -268,18 +233,8 @@ async function decryptAesCtr(key, ciphertext, counter) {
length: 128,
};
const cryptoKey = await crypto.subtle.importKey(
'raw',
key,
algorithm,
extractable,
['decrypt']
);
const plaintext = await crypto.subtle.decrypt(
algorithm,
cryptoKey,
ciphertext
);
const cryptoKey = await crypto.subtle.importKey('raw', key, algorithm, extractable, ['decrypt']);
const plaintext = await crypto.subtle.decrypt(algorithm, cryptoKey, ciphertext);
return plaintext;
}
@ -290,13 +245,7 @@ async function _encrypt_aes_gcm(key, iv, plaintext) {
};
const extractable = false;
const cryptoKey = await crypto.subtle.importKey(
'raw',
key,
algorithm,
extractable,
['encrypt']
);
const cryptoKey = await crypto.subtle.importKey('raw', key, algorithm, extractable, ['encrypt']);
return crypto.subtle.encrypt(algorithm, cryptoKey, plaintext);
}
@ -338,10 +287,7 @@ function getViewOfArrayBuffer(buffer, start, finish) {
}
function concatenateBytes(...elements) {
const length = elements.reduce(
(total, element) => total + element.byteLength,
0
);
const length = elements.reduce((total, element) => total + element.byteLength, 0);
const result = new Uint8Array(length);
let position = 0;

@ -26,8 +26,7 @@ exports.open = (name, version, { onUpgradeNeeded } = {}) => {
reject(
new Error(
'Database upgrade required:' +
` oldVersion: ${oldVersion}, newVersion: ${newVersion}`
'Database upgrade required:' + ` oldVersion: ${oldVersion}, newVersion: ${newVersion}`
)
);
};

@ -1,3 +1 @@
export function deferredToPromise<T>(
deferred: JQuery.Deferred<any, any, any>
): Promise<T>;
export function deferredToPromise<T>(deferred: JQuery.Deferred<any, any, any>): Promise<T>;

@ -12,9 +12,7 @@ exports.setup = (locale, messages) => {
function getMessage(key, substitutions) {
const entry = messages[key];
if (!entry) {
log.error(
`i18n: Attempted to get translation for nonexistent key '${key}'`
);
log.error(`i18n: Attempted to get translation for nonexistent key '${key}'`);
return '';
}

@ -151,9 +151,6 @@ function isLinkSneaky(href) {
// We can't use `url.pathname` (and so on) because it automatically encodes strings.
// For example, it turns `/aquí` into `/aqu%C3%AD`.
const startOfPathAndHash = href.indexOf('/', url.protocol.length + 4);
const pathAndHash =
startOfPathAndHash === -1 ? '' : href.substr(startOfPathAndHash);
return [...pathAndHash].some(
character => !VALID_URI_CHARACTERS.has(character)
);
const pathAndHash = startOfPathAndHash === -1 ? '' : href.substr(startOfPathAndHash);
return [...pathAndHash].some(character => !VALID_URI_CHARACTERS.has(character));
}

@ -1,8 +1,4 @@
import {
Quote,
AttachmentPointer,
Preview,
} from '../../ts/session/messages/outgoing';
import { Quote, AttachmentPointer, Preview } from '../../ts/session/messages/outgoing';
interface UploadResponse {
url: string;

@ -13,12 +13,9 @@ const PUBLICCHAT_CHAN_POLL_EVERY = 20 * 1000; // 20s
const PUBLICCHAT_DELETION_POLL_EVERY = 5 * 1000; // 5s
const PUBLICCHAT_MOD_POLL_EVERY = 30 * 1000; // 30s
const LOKIFOUNDATION_DEVFILESERVER_PUBKEY =
'BSZiMVxOco/b3sYfaeyiMWv/JnqokxGXkHoclEx8TmZ6';
const LOKIFOUNDATION_FILESERVER_PUBKEY =
'BWJQnVm97sQE3Q1InB4Vuo+U/T1hmwHBv0ipkiv8tzEc';
const LOKIFOUNDATION_APNS_PUBKEY =
'BWQqZYWRl0LlotTcUSRJZPvNi8qyt1YSQH3li4EHQNBJ';
const LOKIFOUNDATION_DEVFILESERVER_PUBKEY = 'BSZiMVxOco/b3sYfaeyiMWv/JnqokxGXkHoclEx8TmZ6';
const LOKIFOUNDATION_FILESERVER_PUBKEY = 'BWJQnVm97sQE3Q1InB4Vuo+U/T1hmwHBv0ipkiv8tzEc';
const LOKIFOUNDATION_APNS_PUBKEY = 'BWQqZYWRl0LlotTcUSRJZPvNi8qyt1YSQH3li4EHQNBJ';
const urlPubkeyMap = {
'https://file-dev.getsession.org': LOKIFOUNDATION_DEVFILESERVER_PUBKEY,
@ -64,27 +61,15 @@ class LokiAppDotNetServerAPI {
// channel getter/factory
async findOrCreateChannel(chatAPI, channelId, conversationId) {
let thisChannel = this.channels.find(
channel => channel.channelId === channelId
);
let thisChannel = this.channels.find(channel => channel.channelId === channelId);
if (!thisChannel) {
// make sure we're subscribed
// eventually we'll need to move to account registration/add server
await this.serverRequest(`channels/${channelId}/subscribe`, {
method: 'POST',
});
thisChannel = new LokiPublicChannelAPI(
chatAPI,
this,
channelId,
conversationId
);
log.info(
'LokiPublicChannelAPI started for',
channelId,
'on',
this.baseServerUrl
);
thisChannel = new LokiPublicChannelAPI(chatAPI, this, channelId, conversationId);
log.info('LokiPublicChannelAPI started for', channelId, 'on', this.baseServerUrl);
this.channels.push(thisChannel);
}
return thisChannel;
@ -127,9 +112,7 @@ class LokiAppDotNetServerAPI {
// Hard coded
let pubKeyAB;
if (urlPubkeyMap && urlPubkeyMap[this.baseServerUrl]) {
pubKeyAB = window.Signal.Crypto.base64ToArrayBuffer(
urlPubkeyMap[this.baseServerUrl]
);
pubKeyAB = window.Signal.Crypto.base64ToArrayBuffer(urlPubkeyMap[this.baseServerUrl]);
}
// do we have their pubkey locally?
@ -142,8 +125,7 @@ class LokiAppDotNetServerAPI {
window.lokiPublicChatAPI.openGroupPubKeys &&
window.lokiPublicChatAPI.openGroupPubKeys[this.baseServerUrl]
) {
pubKeyAB =
window.lokiPublicChatAPI.openGroupPubKeys[this.baseServerUrl];
pubKeyAB = window.lokiPublicChatAPI.openGroupPubKeys[this.baseServerUrl];
}
}
// else will fail validation later
@ -189,10 +171,7 @@ class LokiAppDotNetServerAPI {
// no big deal if it fails...
if (res.err || !res.response || !res.response.data) {
if (res.err) {
log.error(
`setProfileName Error ${res.err} ${res.statusCode}`,
this.baseServerUrl
);
log.error(`setProfileName Error ${res.err} ${res.statusCode}`, this.baseServerUrl);
}
return [];
}
@ -243,9 +222,7 @@ class LokiAppDotNetServerAPI {
if (this.token) {
return this.token;
}
token = await Signal.Data.getPublicServerTokenByServerUrl(
this.baseServerUrl
);
token = await Signal.Data.getPublicServerTokenByServerUrl(this.baseServerUrl);
}
if (!token) {
token = await this.refreshServerToken();
@ -360,25 +337,12 @@ class LokiAppDotNetServerAPI {
// not really an error, from a client's pov, network servers can fail...
if (e.code === 'ECONNREFUSED') {
// down
log.warn(
'requestToken request can not connect',
this.baseServerUrl,
e.message
);
log.warn('requestToken request can not connect', this.baseServerUrl, e.message);
} else if (e.code === 'ECONNRESET') {
// got disconnected
log.warn(
'requestToken request lost connection',
this.baseServerUrl,
e.message
);
log.warn('requestToken request lost connection', this.baseServerUrl, e.message);
} else {
log.error(
'requestToken request failed',
this.baseServerUrl,
e.code,
e.message
);
log.error('requestToken request failed', this.baseServerUrl, e.code, e.message);
}
return null;
}
@ -448,9 +412,7 @@ class LokiAppDotNetServerAPI {
log.warn('No channelId provided to getModerators!');
return [];
}
const res = await this.serverRequest(
`loki/v1/channels/${channelId}/moderators`
);
const res = await this.serverRequest(`loki/v1/channels/${channelId}/moderators`);
return (!res.err && res.response && res.response.moderators) || [];
}
@ -590,11 +552,7 @@ class LokiAppDotNetServerAPI {
if (res.err || !res.response || !res.response.data) {
if (res.err) {
log.error(
`loki_app_dot_net:::getUsers - Error: ${res.err} for ${pubKeys.join(
','
)}`
);
log.error(`loki_app_dot_net:::getUsers - Error: ${res.err} for ${pubKeys.join(',')}`);
}
return [];
}
@ -639,10 +597,7 @@ class LokiAppDotNetServerAPI {
throw new Error(`Failed to upload avatar to ${this.baseServerUrl}`);
}
const url =
response.data &&
response.data.avatar_image &&
response.data.avatar_image.url;
const url = response.data && response.data.avatar_image && response.data.avatar_image.url;
if (!url) {
throw new Error(`Failed to upload data: Invalid url.`);
@ -720,9 +675,7 @@ class LokiAppDotNetServerAPI {
});
if (window.lokiFeatureFlags.useFileOnionRequestsV2) {
const buffer = dcodeIO.ByteBuffer.fromBase64(
res.response
).toArrayBuffer();
const buffer = dcodeIO.ByteBuffer.fromBase64(res.response).toArrayBuffer();
return buffer;
}
return new Uint8Array(res.response.data).buffer;
@ -737,9 +690,7 @@ class LokiPublicChannelAPI {
this.channelId = channelId;
this.baseChannelUrl = `channels/${this.channelId}`;
this.conversationId = conversationId;
this.conversation = window
.getConversationController()
.getOrThrow(conversationId);
this.conversation = window.getConversationController().getOrThrow(conversationId);
this.lastMessageServerID = null;
this.modStatus = false;
this.deleteLastId = 1;
@ -755,9 +706,7 @@ class LokiPublicChannelAPI {
// end properties
log.info(
`registered LokiPublicChannel ${channelId} on ${this.serverAPI.baseServerUrl}`
);
log.info(`registered LokiPublicChannel ${channelId} on ${this.serverAPI.baseServerUrl}`);
// start polling
this.open();
}
@ -775,12 +724,9 @@ class LokiPublicChannelAPI {
}
async banUser(pubkey) {
const res = await this.serverRequest(
`loki/v1/moderation/blacklist/@${pubkey}`,
{
method: 'POST',
}
);
const res = await this.serverRequest(`loki/v1/moderation/blacklist/@${pubkey}`, {
method: 'POST',
});
if (res.err || !res.response || !res.response.data) {
if (res.err) {
@ -793,9 +739,7 @@ class LokiPublicChannelAPI {
}
open() {
log.info(
`LokiPublicChannel open ${this.channelId} on ${this.serverAPI.baseServerUrl}`
);
log.info(`LokiPublicChannel open ${this.channelId} on ${this.serverAPI.baseServerUrl}`);
if (this.running) {
log.warn(
`LokiPublicChannel already open ${this.channelId} on ${this.serverAPI.baseServerUrl}`
@ -818,9 +762,7 @@ class LokiPublicChannelAPI {
}
stop() {
log.info(
`LokiPublicChannel close ${this.channelId} on ${this.serverAPI.baseServerUrl}`
);
log.info(`LokiPublicChannel close ${this.channelId} on ${this.serverAPI.baseServerUrl}`);
if (!this.running) {
log.warn(
`LokiPublicChannel already open ${this.channelId} on ${this.serverAPI.baseServerUrl}`
@ -862,11 +804,7 @@ class LokiPublicChannelAPI {
try {
await this.pollOnceForModerators();
} catch (e) {
log.warn(
'Error while polling for public chat moderators:',
e.code,
e.message
);
log.warn('Error while polling for public chat moderators:', e.code, e.message);
}
if (this.running) {
this.timers.moderator = setTimeout(() => {
@ -878,9 +816,7 @@ class LokiPublicChannelAPI {
// get moderator status
async pollOnceForModerators() {
// get moderator status
const res = await this.serverRequest(
`loki/v1/channels/${this.channelId}/moderators`
);
const res = await this.serverRequest(`loki/v1/channels/${this.channelId}/moderators`);
const ourNumberDevice = window.libsession.Utils.UserUtils.getOurPubKeyStrFromCache();
// Get the list of moderators if no errors occurred
@ -911,15 +847,12 @@ class LokiPublicChannelAPI {
log.warn(`public chat channel state unknown, skipping set: ${res.err}`);
return false;
}
let notes =
res.response && res.response.data && res.response.data.annotations;
let notes = res.response && res.response.data && res.response.data.annotations;
if (!notes) {
// ok if nothing is set yet
notes = [];
}
let settingNotes = notes.filter(
note => note.type === SETTINGS_CHANNEL_ANNOTATION_TYPE
);
let settingNotes = notes.filter(note => note.type === SETTINGS_CHANNEL_ANNOTATION_TYPE);
if (!settingNotes) {
// default name, description, avatar
settingNotes = [
@ -936,10 +869,10 @@ class LokiPublicChannelAPI {
// update settings
settingNotes[0].value = Object.assign(settingNotes[0].value, settings);
// commit settings
const updateRes = await this.serverRequest(
`loki/v1/${this.baseChannelUrl}`,
{ method: 'PUT', objBody: { annotations: settingNotes } }
);
const updateRes = await this.serverRequest(`loki/v1/${this.baseChannelUrl}`, {
method: 'PUT',
objBody: { annotations: settingNotes },
});
if (updateRes.err || !updateRes.response || !updateRes.response.data) {
if (updateRes.err) {
log.error(`setChannelSettings Error ${updateRes.err}`);
@ -967,17 +900,13 @@ class LokiPublicChannelAPI {
{ method: 'DELETE', params: { ids: serverIds } }
);
if (!res.err) {
const deletedIds = res.response.data
.filter(d => d.is_deleted)
.map(d => d.id);
const deletedIds = res.response.data.filter(d => d.is_deleted).map(d => d.id);
if (deletedIds.length > 0) {
log.info(`deleted ${serverIds} on ${this.baseChannelUrl}`);
}
const failedIds = res.response.data
.filter(d => !d.is_deleted)
.map(d => d.id);
const failedIds = res.response.data.filter(d => !d.is_deleted).map(d => d.id);
if (failedIds.length > 0) {
log.warn(`failed to delete ${failedIds} on ${this.baseChannelUrl}`);
@ -985,10 +914,7 @@ class LokiPublicChannelAPI {
// Note: if there is no entry for message, we assume it wasn't found
// on the server, so it is not treated as explicitly failed
const ignoredIds = _.difference(
serverIds,
_.union(failedIds, deletedIds)
);
const ignoredIds = _.difference(serverIds, _.union(failedIds, deletedIds));
if (ignoredIds.length > 0) {
log.warn(`No response for ${ignoredIds} on ${this.baseChannelUrl}`);
@ -997,9 +923,7 @@ class LokiPublicChannelAPI {
return { deletedIds, ignoredIds };
}
if (canThrow) {
throw new textsecure.PublicChatError(
'Failed to delete public chat message'
);
throw new textsecure.PublicChatError('Failed to delete public chat message');
}
return { deletedIds: [], ignoredIds: [] };
}
@ -1015,11 +939,7 @@ class LokiPublicChannelAPI {
try {
await this.pollForChannelOnce();
} catch (e) {
log.warn(
'Error while polling for public chat room details',
e.code,
e.message
);
log.warn('Error while polling for public chat room details', e.code, e.message);
}
if (this.running) {
this.timers.channel = setTimeout(() => {
@ -1072,16 +992,11 @@ class LokiPublicChannelAPI {
} else {
// relative URL avatar
const avatarAbsUrl = this.serverAPI.baseServerUrl + note.value.avatar;
const {
writeNewAttachmentData,
deleteAttachmentData,
} = window.Signal.Migrations;
const { writeNewAttachmentData, deleteAttachmentData } = window.Signal.Migrations;
// do we already have this image? no, then
// download a copy and save it
const imageData = await this.serverAPI.downloadAttachment(
avatarAbsUrl
);
const imageData = await this.serverAPI.downloadAttachment(avatarAbsUrl);
const newAttributes = await window.Signal.Types.Conversation.maybeUpdateAvatar(
this.conversation.attributes,
@ -1111,11 +1026,7 @@ class LokiPublicChannelAPI {
try {
await this.pollOnceForDeletions();
} catch (e) {
log.warn(
'Error while polling for public chat deletions:',
e.code,
e.message
);
log.warn('Error while polling for public chat deletions:', e.code, e.message);
}
if (this.running) {
this.timers.delete = setTimeout(() => {
@ -1138,18 +1049,10 @@ class LokiPublicChannelAPI {
// grab the next 200 deletions from where we last checked
// eslint-disable-next-line no-await-in-loop
const res = await this.serverRequest(
`loki/v1/channel/${this.channelId}/deletes`,
{ params }
);
const res = await this.serverRequest(`loki/v1/channel/${this.channelId}/deletes`, { params });
// if any problems, abort out
if (
res.err ||
!res.response ||
!res.response.data ||
!res.response.meta
) {
if (res.err || !res.response || !res.response.data || !res.response.meta) {
if (res.statusCode === 403) {
// token is now invalid
this.serverAPI.getOrRefreshServerToken(true);
@ -1157,9 +1060,7 @@ class LokiPublicChannelAPI {
if (res.err) {
log.error(`pollOnceForDeletions Error ${res.err}`);
} else {
log.error(
`pollOnceForDeletions Error: Received incorrect response ${res.response}`
);
log.error(`pollOnceForDeletions Error: Received incorrect response ${res.response}`);
}
break;
}
@ -1175,20 +1076,11 @@ class LokiPublicChannelAPI {
// update where we last checked
this.deleteLastId = res.response.meta.max_id;
more =
res.response.meta.more &&
res.response.data.length >= params.count &&
this.running;
more = res.response.meta.more && res.response.data.length >= params.count && this.running;
}
}
static getSigData(
sigVer,
noteValue,
attachmentAnnotations,
previewAnnotations,
adnMessage
) {
static getSigData(sigVer, noteValue, attachmentAnnotations, previewAnnotations, adnMessage) {
let sigString = '';
sigString += adnMessage.text.trim();
sigString += noteValue.timestamp;
@ -1210,10 +1102,7 @@ class LokiPublicChannelAPI {
}
async getMessengerData(adnMessage) {
if (
!Array.isArray(adnMessage.annotations) ||
adnMessage.annotations.length === 0
) {
if (!Array.isArray(adnMessage.annotations) || adnMessage.annotations.length === 0) {
return false;
}
const noteValue = adnMessage.annotations[0].value;
@ -1293,8 +1182,7 @@ class LokiPublicChannelAPI {
return {
timestamp,
serverTimestamp:
new Date(`${adnMessage.created_at}`).getTime() || timestamp,
serverTimestamp: new Date(`${adnMessage.created_at}`).getTime() || timestamp,
attachments,
preview,
quote,
@ -1309,11 +1197,7 @@ class LokiPublicChannelAPI {
try {
await this.pollOnceForMessages();
} catch (e) {
log.warn(
'Error while polling for public chat messages:',
e.code,
e.message
);
log.warn('Error while polling for public chat messages:', e.code, e.message);
}
if (this.running) {
this.timers.message = setTimeout(() => {
@ -1386,8 +1270,7 @@ class LokiPublicChannelAPI {
// get our profile name
const ourNumberDevice = window.libsession.Utils.UserUtils.getOurPubKeyStrFromCache();
// if no primaryDevicePubKey fall back to ourNumberDevice
const ourNumberProfile =
window.storage.get('primaryDevicePubKey') || ourNumberDevice;
const ourNumberProfile = window.storage.get('primaryDevicePubKey') || ourNumberDevice;
let lastProfileName = false;
// the signature forces this to be async
@ -1437,11 +1320,7 @@ class LokiPublicChannelAPI {
// message is one of the object of this.lastMessagesCache
// testedMessage is the adnMessage object
const isDuplicate = (message, testedMessage) =>
DataMessage.isDuplicate(
message,
testedMessage,
testedMessage.user.username
);
DataMessage.isDuplicate(message, testedMessage, testedMessage.user.username);
const isThisMessageDuplicate = this.lastMessagesCache.some(m =>
isDuplicate(m, adnMessage)
);
@ -1503,8 +1382,7 @@ class LokiPublicChannelAPI {
receivedAt,
isPublic: true,
message: {
body:
adnMessage.text === timestamp.toString() ? '' : adnMessage.text,
body: adnMessage.text === timestamp.toString() ? '' : adnMessage.text,
attachments,
group: {
id: this.conversationId,
@ -1571,9 +1449,7 @@ class LokiPublicChannelAPI {
// if we received one of our own messages
if (lastProfileName !== false) {
// get current profileName
const profileConvo = window
.getConversationController()
.get(ourNumberProfile);
const profileConvo = window.getConversationController().get(ourNumberProfile);
const profileName = profileConvo.getProfileName();
// check to see if it out of sync
if (profileName !== lastProfileName) {
@ -1668,12 +1544,8 @@ class LokiPublicChannelAPI {
async sendMessage(data, messageTimeStamp) {
const { quote, attachments, preview } = data;
const text = data.body || messageTimeStamp.toString();
const attachmentAnnotations = attachments.map(
LokiPublicChannelAPI.getAnnotationFromAttachment
);
const previewAnnotations = preview.map(
LokiPublicChannelAPI.getAnnotationFromPreview
);
const attachmentAnnotations = attachments.map(LokiPublicChannelAPI.getAnnotationFromAttachment);
const previewAnnotations = preview.map(LokiPublicChannelAPI.getAnnotationFromPreview);
const payload = {
text,
@ -1721,10 +1593,7 @@ class LokiPublicChannelAPI {
previewAnnotations.map(anno => anno.value),
mockAdnMessage
);
const sig = await libsignal.Curve.async.calculateSignature(
privKey,
sigData
);
const sig = await libsignal.Curve.async.calculateSignature(privKey, sigData);
payload.annotations[0].value.sig = StringView.arrayBufferToHex(sig);
payload.annotations[0].value.sigver = sigVer;
const res = await this.serverRequest(`${this.baseChannelUrl}/messages`, {

@ -63,9 +63,7 @@ class LokiFileServerFactoryAPI {
}
establishHomeConnection(serverUrl) {
let thisServer = this.servers.find(
server => server._server.baseServerUrl === serverUrl
);
let thisServer = this.servers.find(server => server._server.baseServerUrl === serverUrl);
if (!thisServer) {
thisServer = new LokiHomeServerInstance(this.ourKey);
log.info(`Registering HomeServer ${serverUrl}`);
@ -77,9 +75,7 @@ class LokiFileServerFactoryAPI {
}
async establishConnection(serverUrl) {
let thisServer = this.servers.find(
server => server._server.baseServerUrl === serverUrl
);
let thisServer = this.servers.find(server => server._server.baseServerUrl === serverUrl);
if (!thisServer) {
thisServer = new LokiFileServerInstance(this.ourKey);
log.info(`Registering FileServer ${serverUrl}`);

@ -45,9 +45,7 @@ class LokiMessageAPI {
};
if (isPublic) {
window.log.warn(
'this sendMessage() should not be called anymore with an open group message'
);
window.log.warn('this sendMessage() should not be called anymore with an open group message');
return;
}
@ -98,10 +96,7 @@ class LokiMessageAPI {
throw e;
}
if (!snode) {
throw new window.textsecure.EmptySwarmError(
pubKey,
'Ran out of swarm nodes to query'
);
throw new window.textsecure.EmptySwarmError(pubKey, 'Ran out of swarm nodes to query');
} else {
log.info(
`loki_message:::sendMessage - Successfully stored message to ${pubKey} via ${snode.ip}:${snode.port}`

@ -1,6 +1,3 @@
export async function sleepFor(ms: number);
export async function abortableIterator(
array: Array<any>,
action: (any) => void
);
export async function abortableIterator(array: Array<any>, action: (any) => void);

@ -1,7 +1,4 @@
import {
LokiAppDotNetServerInterface,
LokiPublicChannelAPI,
} from './loki_app_dot_net_api';
import { LokiAppDotNetServerInterface, LokiPublicChannelAPI } from './loki_app_dot_net_api';
export interface LokiPublicChatFactoryInterface {
ourKey: string;
@ -12,16 +9,11 @@ export interface LokiPublicChatFactoryInterface {
channelId: number,
conversationId: string
): Promise<LokiPublicChannelAPI | null>;
getListOfMembers(): Promise<
Array<{ authorPhoneNumber: string; authorProfileName?: string }>
>;
setListOfMembers(
members: Array<{ authorPhoneNumber: string; authorProfileName?: string }>
);
getListOfMembers(): Promise<Array<{ authorPhoneNumber: string; authorProfileName?: string }>>;
setListOfMembers(members: Array<{ authorPhoneNumber: string; authorProfileName?: string }>);
}
declare class LokiPublicChatFactoryAPI
implements LokiPublicChatFactoryInterface {
declare class LokiPublicChatFactoryAPI implements LokiPublicChatFactoryInterface {
constructor(ourKey: string);
}

@ -26,20 +26,14 @@ class LokiPublicChatFactoryAPI extends EventEmitter {
// server getter/factory
async findOrCreateServer(serverUrl) {
let thisServer = this.servers.find(
server => server.baseServerUrl === serverUrl
);
let thisServer = this.servers.find(server => server.baseServerUrl === serverUrl);
if (!thisServer) {
log.info(`loki_public_chat::findOrCreateServer - creating ${serverUrl}`);
const serverIsValid = await OpenGroupUtils.validOpenGroupServer(
serverUrl
);
const serverIsValid = await OpenGroupUtils.validOpenGroupServer(serverUrl);
if (!serverIsValid) {
// FIXME: add toast?
log.error(
`loki_public_chat::findOrCreateServer - error: ${serverUrl} is not valid`
);
log.error(`loki_public_chat::findOrCreateServer - error: ${serverUrl} is not valid`);
return null;
}
@ -53,18 +47,14 @@ class LokiPublicChatFactoryAPI extends EventEmitter {
if (this.openGroupPubKeys[serverUrl]) {
thisServer.getPubKeyForUrl();
if (!thisServer.pubKeyHex) {
log.warn(
`loki_public_chat::findOrCreateServer - failed to set public key`
);
log.warn(`loki_public_chat::findOrCreateServer - failed to set public key`);
}
}
}
const gotToken = await thisServer.getOrRefreshServerToken();
if (!gotToken) {
log.warn(
`loki_public_chat::findOrCreateServer - Invalid server ${serverUrl}`
);
log.warn(`loki_public_chat::findOrCreateServer - Invalid server ${serverUrl}`);
return null;
}
@ -85,9 +75,7 @@ class LokiPublicChatFactoryAPI extends EventEmitter {
// deallocate resources server uses
unregisterChannel(serverUrl, channelId) {
const i = this.servers.findIndex(
server => server.baseServerUrl === serverUrl
);
const i = this.servers.findIndex(server => server.baseServerUrl === serverUrl);
if (i === -1) {
log.warn(`Tried to unregister from nonexistent server ${serverUrl}`);
return;

@ -4,8 +4,7 @@ const LokiAppDotNetAPI = require('./loki_app_dot_net_api');
class LokiPushNotificationServerApi {
constructor() {
this.ourKey =
'642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049';
this.ourKey = '642a6585919742e5a2d4dc51244964fbcd8bcab2b75612407de58b810740d049';
this.serverUrl = 'https://live.apns.getsession.org';
this._server = new LokiAppDotNetAPI(this.ourKey, this.serverUrl);

@ -32,9 +32,7 @@ class LokiSnodeAPI {
// Timeouts
const maxTimeoutVal = 2 ** 31 - 1;
const timeoutPromise = () =>
new Promise((_resolve, reject) =>
setTimeout(() => reject(), timeout || maxTimeoutVal)
);
new Promise((_resolve, reject) => setTimeout(() => reject(), timeout || maxTimeoutVal));
// Get nodes capable of doing LNS
const lnsNodes = await window.SnodePool.getNodesMinVersion(
@ -72,9 +70,7 @@ class LokiSnodeAPI {
if (res && res.result && res.result.status === 'OK') {
const hasMapping = res.result.entries && res.result.entries.length > 0;
const resValue = hasMapping
? res.result.entries[0].encrypted_value
: null;
const resValue = hasMapping ? res.result.entries[0].encrypted_value : null;
confirmedNodes.push(resValue);
@ -84,10 +80,7 @@ class LokiSnodeAPI {
return;
}
const [winner, count] = _.maxBy(
_.entries(_.countBy(confirmedNodes)),
x => x[1]
);
const [winner, count] = _.maxBy(_.entries(_.countBy(confirmedNodes)), x => x[1]);
if (count >= numRequiredConfirms) {
ciphertextHex = winner === String(null) ? null : winner;

@ -26,8 +26,7 @@ exports.getMessageExportLastIndex = connection =>
exports._getItem(connection, MESSAGE_LAST_INDEX_KEY);
exports.setMessageExportLastIndex = (connection, lastIndex) =>
exports._setItem(connection, MESSAGE_LAST_INDEX_KEY, lastIndex);
exports.getMessageExportCount = connection =>
exports._getItem(connection, MESSAGE_COUNT_KEY);
exports.getMessageExportCount = connection => exports._getItem(connection, MESSAGE_COUNT_KEY);
exports.setMessageExportCount = (connection, count) =>
exports._setItem(connection, MESSAGE_COUNT_KEY, count);
@ -52,8 +51,7 @@ exports._getItem = (connection, key) => {
return new Promise((resolve, reject) => {
request.onerror = event => reject(event.target.error);
request.onsuccess = event =>
resolve(event.target.result ? event.target.result.value : null);
request.onsuccess = event => resolve(event.target.result ? event.target.result.value : null);
});
};

@ -15,42 +15,24 @@ const { Message } = require('../../ts/components/conversation/Message');
// Components
const { EditProfileDialog } = require('../../ts/components/EditProfileDialog');
const { UserDetailsDialog } = require('../../ts/components/UserDetailsDialog');
const {
SessionSeedModal,
} = require('../../ts/components/session/SessionSeedModal');
const {
SessionIDResetDialog,
} = require('../../ts/components/session/SessionIDResetDialog');
const {
SessionRegistrationView,
} = require('../../ts/components/session/SessionRegistrationView');
const { SessionSeedModal } = require('../../ts/components/session/SessionSeedModal');
const { SessionIDResetDialog } = require('../../ts/components/session/SessionIDResetDialog');
const { SessionRegistrationView } = require('../../ts/components/session/SessionRegistrationView');
const {
SessionInboxView,
} = require('../../ts/components/session/SessionInboxView');
const {
SessionPasswordModal,
} = require('../../ts/components/session/SessionPasswordModal');
const {
SessionConfirm,
} = require('../../ts/components/session/SessionConfirm');
const { SessionInboxView } = require('../../ts/components/session/SessionInboxView');
const { SessionPasswordModal } = require('../../ts/components/session/SessionPasswordModal');
const { SessionConfirm } = require('../../ts/components/session/SessionConfirm');
const {
UpdateGroupNameDialog,
} = require('../../ts/components/conversation/UpdateGroupNameDialog');
const { UpdateGroupNameDialog } = require('../../ts/components/conversation/UpdateGroupNameDialog');
const {
UpdateGroupMembersDialog,
} = require('../../ts/components/conversation/UpdateGroupMembersDialog');
const {
InviteContactsDialog,
} = require('../../ts/components/conversation/InviteContactsDialog');
const { InviteContactsDialog } = require('../../ts/components/conversation/InviteContactsDialog');
const {
AdminLeaveClosedGroupDialog,
} = require('../../ts/components/conversation/AdminLeaveClosedGroupDialog');
const {
AddModeratorsDialog,
} = require('../../ts/components/conversation/ModeratorsAddDialog');
const { AddModeratorsDialog } = require('../../ts/components/conversation/ModeratorsAddDialog');
const {
RemoveModeratorsDialog,
} = require('../../ts/components/conversation/ModeratorsRemoveDialog');
@ -68,13 +50,7 @@ const SettingsType = require('../../ts/types/Settings');
// Views
const Initialization = require('./views/initialization');
function initializeMigrations({
userDataPath,
Attachments,
Type,
VisualType,
logger,
}) {
function initializeMigrations({ userDataPath, Attachments, Type, VisualType, logger }) {
if (!Attachments) {
return null;
}
@ -146,8 +122,7 @@ function initializeMigrations({
logger,
}),
writeNewAttachmentData: createWriterForNew(attachmentsPath),
writeAttachment: ({ data, path }) =>
createWriterForExisting(attachmentsPath)({ data, path }),
writeAttachment: ({ data, path }) => createWriterForExisting(attachmentsPath)({ data, path }),
};
}

@ -4,15 +4,9 @@ const AttachmentTS = require('../../../ts/types/Attachment');
const GoogleChrome = require('../../../ts/util/GoogleChrome');
const MIME = require('../../../ts/types/MIME');
const { toLogFormat } = require('./errors');
const {
arrayBufferToBlob,
blobToArrayBuffer,
dataURLToBlob,
} = require('blob-util');
const { arrayBufferToBlob, blobToArrayBuffer, dataURLToBlob } = require('blob-util');
const { autoOrientImage } = require('../auto_orient_image');
const {
migrateDataToFileSystem,
} = require('./attachment/migrate_data_to_file_system');
const { migrateDataToFileSystem } = require('./attachment/migrate_data_to_file_system');
// // Incoming message attachment fields
// {
@ -61,10 +55,7 @@ exports.autoOrientJPEG = async attachment => {
return attachment;
}
const dataBlob = await arrayBufferToBlob(
attachment.data,
attachment.contentType
);
const dataBlob = await arrayBufferToBlob(attachment.data, attachment.contentType);
const newDataBlob = await dataURLToBlob(await autoOrientImage(dataBlob));
const newDataArrayBuffer = await blobToArrayBuffer(newDataBlob);
@ -125,10 +116,7 @@ exports.replaceUnicodeV2 = async attachment => {
return attachment;
}
const fileName = attachment.fileName.replace(
V2_UNWANTED_UNICODE,
UNICODE_REPLACEMENT_CHARACTER
);
const fileName = attachment.fileName.replace(V2_UNWANTED_UNICODE, UNICODE_REPLACEMENT_CHARACTER);
return {
...attachment,
@ -138,10 +126,7 @@ exports.replaceUnicodeV2 = async attachment => {
exports.removeSchemaVersion = ({ attachment, logger }) => {
if (!exports.isValid(attachment)) {
logger.error(
'Attachment.removeSchemaVersion: Invalid input attachment:',
attachment
);
logger.error('Attachment.removeSchemaVersion: Invalid input attachment:', attachment);
return attachment;
}
@ -294,10 +279,7 @@ exports.captureDimensionsAndScreenshot = async (
logger,
})
);
screenshotObjectUrl = makeObjectUrl(
screenshotBuffer,
THUMBNAIL_CONTENT_TYPE
);
screenshotObjectUrl = makeObjectUrl(screenshotBuffer, THUMBNAIL_CONTENT_TYPE);
const { width, height } = await getImageDimensions({
objectUrl: screenshotObjectUrl,
logger,

@ -7,10 +7,7 @@ const { isArrayBuffer, isFunction, isUndefined, omit } = require('lodash');
// migrateDataToFileSystem :: Attachment ->
// Context ->
// Promise Attachment
exports.migrateDataToFileSystem = async (
attachment,
{ writeNewAttachmentData } = {}
) => {
exports.migrateDataToFileSystem = async (attachment, { writeNewAttachmentData } = {}) => {
if (!isFunction(writeNewAttachmentData)) {
throw new TypeError("'writeNewAttachmentData' must be a function");
}
@ -25,15 +22,12 @@ exports.migrateDataToFileSystem = async (
const isValidData = isArrayBuffer(data);
if (!isValidData) {
throw new TypeError(
'Expected `attachment.data` to be an array buffer;' +
` got: ${typeof attachment.data}`
'Expected `attachment.data` to be an array buffer;' + ` got: ${typeof attachment.data}`
);
}
const path = await writeNewAttachmentData(data);
const attachmentWithoutData = omit(Object.assign({}, attachment, { path }), [
'data',
]);
const attachmentWithoutData = omit(Object.assign({}, attachment, { path }), ['data']);
return attachmentWithoutData;
};

@ -5,10 +5,7 @@ const { SignalService } = require('../../../ts/protobuf');
const DEFAULT_PHONE_TYPE = SignalService.DataMessage.Contact.Phone.Type.HOME;
exports.parseAndWriteAvatar = upgradeAttachment => async (
contact,
context = {}
) => {
exports.parseAndWriteAvatar = upgradeAttachment => async (contact, context = {}) => {
const { message, logger } = context;
const { avatar } = contact;
@ -31,10 +28,7 @@ exports.parseAndWriteAvatar = upgradeAttachment => async (
messageId: idForLogging(message),
});
if (error) {
logger.error(
'Contact.parseAndWriteAvatar: contact was malformed.',
toLogFormat(error)
);
logger.error('Contact.parseAndWriteAvatar: contact was malformed.', toLogFormat(error));
}
return parsedContact;
@ -60,9 +54,7 @@ exports._validate = (contact, options = {}) => {
const { name, number, organization } = contact;
if ((!name || !name.displayName) && !organization) {
return new Error(
`Message ${messageId}: Contact had neither 'displayName' nor 'organization'`
);
return new Error(`Message ${messageId}: Contact had neither 'displayName' nor 'organization'`);
}
if (!number || !number.length) {

@ -18,14 +18,10 @@ function buildAvatarUpdater({ field }) {
const avatar = conversation[field];
const { writeNewAttachmentData, deleteAttachmentData } = options;
if (!isFunction(writeNewAttachmentData)) {
throw new Error(
'Conversation.buildAvatarUpdater: writeNewAttachmentData must be a function'
);
throw new Error('Conversation.buildAvatarUpdater: writeNewAttachmentData must be a function');
}
if (!isFunction(deleteAttachmentData)) {
throw new Error(
'Conversation.buildAvatarUpdater: deleteAttachmentData must be a function'
);
throw new Error('Conversation.buildAvatarUpdater: deleteAttachmentData must be a function');
}
const newHash = await computeHash(data);
@ -70,9 +66,7 @@ async function upgradeToVersion2(conversation, options) {
const { writeNewAttachmentData } = options;
if (!isFunction(writeNewAttachmentData)) {
throw new Error(
'Conversation.upgradeToVersion2: writeNewAttachmentData must be a function'
);
throw new Error('Conversation.upgradeToVersion2: writeNewAttachmentData must be a function');
}
let { avatar, profileAvatar, profileKey } = conversation;
@ -123,9 +117,7 @@ async function deleteExternalFiles(conversation, options = {}) {
const { deleteAttachmentData } = options;
if (!isFunction(deleteAttachmentData)) {
throw new Error(
'Conversation.buildAvatarUpdater: deleteAttachmentData must be a function'
);
throw new Error('Conversation.buildAvatarUpdater: deleteAttachmentData must be a function');
}
const { avatar, profileAvatar } = conversation;

@ -60,15 +60,12 @@ exports.isValid = () => true;
// Schema
exports.initializeSchemaVersion = ({ message, logger }) => {
const isInitialized =
SchemaVersion.isValid(message.schemaVersion) && message.schemaVersion >= 1;
const isInitialized = SchemaVersion.isValid(message.schemaVersion) && message.schemaVersion >= 1;
if (isInitialized) {
return message;
}
const numAttachments = Array.isArray(message.attachments)
? message.attachments.length
: 0;
const numAttachments = Array.isArray(message.attachments) ? message.attachments.length : 0;
const hasAttachments = numAttachments > 0;
if (!hasAttachments) {
return Object.assign({}, message, {
@ -79,9 +76,7 @@ exports.initializeSchemaVersion = ({ message, logger }) => {
// All attachments should have the same schema version, so we just pick
// the first one:
const firstAttachment = message.attachments[0];
const inheritedSchemaVersion = SchemaVersion.isValid(
firstAttachment.schemaVersion
)
const inheritedSchemaVersion = SchemaVersion.isValid(firstAttachment.schemaVersion)
? firstAttachment.schemaVersion
: INITIAL_SCHEMA_VERSION;
const messageWithInitialSchema = Object.assign({}, message, {
@ -108,17 +103,12 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
return async (message, context) => {
if (!context || !isObject(context.logger)) {
throw new TypeError(
'_withSchemaVersion: context must have logger object'
);
throw new TypeError('_withSchemaVersion: context must have logger object');
}
const { logger } = context;
if (!exports.isValid(message)) {
logger.error(
'Message._withSchemaVersion: Invalid input message:',
message
);
logger.error('Message._withSchemaVersion: Invalid input message:', message);
return message;
}
@ -150,10 +140,7 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
}
if (!exports.isValid(upgradedMessage)) {
logger.error(
'Message._withSchemaVersion: Invalid upgraded message:',
upgradedMessage
);
logger.error('Message._withSchemaVersion: Invalid upgraded message:', upgradedMessage);
return message;
}
@ -166,11 +153,8 @@ exports._withSchemaVersion = ({ schemaVersion, upgrade }) => {
// (Message, Context) ->
// Promise Message
exports._mapAttachments = upgradeAttachment => async (message, context) => {
const upgradeWithContext = attachment =>
upgradeAttachment(attachment, context);
const attachments = await Promise.all(
(message.attachments || []).map(upgradeWithContext)
);
const upgradeWithContext = attachment => upgradeAttachment(attachment, context);
const attachments = await Promise.all((message.attachments || []).map(upgradeWithContext));
return Object.assign({}, message, { attachments });
};
@ -180,21 +164,15 @@ exports._mapAttachments = upgradeAttachment => async (message, context) => {
// Promise Message
exports._mapContact = upgradeContact => async (message, context) => {
const contextWithMessage = Object.assign({}, context, { message });
const upgradeWithContext = contact =>
upgradeContact(contact, contextWithMessage);
const contact = await Promise.all(
(message.contact || []).map(upgradeWithContext)
);
const upgradeWithContext = contact => upgradeContact(contact, contextWithMessage);
const contact = await Promise.all((message.contact || []).map(upgradeWithContext));
return Object.assign({}, message, { contact });
};
// _mapQuotedAttachments :: (QuotedAttachment -> Promise QuotedAttachment) ->
// (Message, Context) ->
// Promise Message
exports._mapQuotedAttachments = upgradeAttachment => async (
message,
context
) => {
exports._mapQuotedAttachments = upgradeAttachment => async (message, context) => {
if (!message.quote) {
return message;
}
@ -216,9 +194,7 @@ exports._mapQuotedAttachments = upgradeAttachment => async (
const quotedAttachments = (message.quote && message.quote.attachments) || [];
const attachments = await Promise.all(
quotedAttachments.map(upgradeWithContext)
);
const attachments = await Promise.all(quotedAttachments.map(upgradeWithContext));
return Object.assign({}, message, {
quote: Object.assign({}, message.quote, {
attachments,
@ -229,10 +205,7 @@ exports._mapQuotedAttachments = upgradeAttachment => async (
// _mapPreviewAttachments :: (PreviewAttachment -> Promise PreviewAttachment) ->
// (Message, Context) ->
// Promise Message
exports._mapPreviewAttachments = upgradeAttachment => async (
message,
context
) => {
exports._mapPreviewAttachments = upgradeAttachment => async (message, context) => {
if (!message.preview) {
return message;
}
@ -252,9 +225,7 @@ exports._mapPreviewAttachments = upgradeAttachment => async (
});
};
const preview = await Promise.all(
(message.preview || []).map(upgradeWithContext)
);
const preview = await Promise.all((message.preview || []).map(upgradeWithContext));
return Object.assign({}, message, {
preview,
});
@ -284,9 +255,7 @@ const toVersion5 = exports._withSchemaVersion({
});
const toVersion6 = exports._withSchemaVersion({
schemaVersion: 6,
upgrade: exports._mapContact(
Contact.parseAndWriteAvatar(Attachment.migrateDataToFileSystem)
),
upgrade: exports._mapContact(Contact.parseAndWriteAvatar(Attachment.migrateDataToFileSystem)),
});
// IMPORTANT: Weve updated our definition of `initializeAttachmentMetadata`, so
// we need to run it again on existing items that have previously been incorrectly
@ -435,39 +404,31 @@ exports.processNewAttachment = async (
}
const rotatedAttachment = await Attachment.autoOrientJPEG(attachment);
const onDiskAttachment = await Attachment.migrateDataToFileSystem(
rotatedAttachment,
{ writeNewAttachmentData }
);
const finalAttachment = await Attachment.captureDimensionsAndScreenshot(
onDiskAttachment,
{
writeNewAttachmentData,
getAbsoluteAttachmentPath,
makeObjectUrl,
revokeObjectUrl,
getImageDimensions,
makeImageThumbnail,
makeVideoScreenshot,
logger,
}
);
const onDiskAttachment = await Attachment.migrateDataToFileSystem(rotatedAttachment, {
writeNewAttachmentData,
});
const finalAttachment = await Attachment.captureDimensionsAndScreenshot(onDiskAttachment, {
writeNewAttachmentData,
getAbsoluteAttachmentPath,
makeObjectUrl,
revokeObjectUrl,
getImageDimensions,
makeImageThumbnail,
makeVideoScreenshot,
logger,
});
return finalAttachment;
};
exports.createAttachmentLoader = loadAttachmentData => {
if (!isFunction(loadAttachmentData)) {
throw new TypeError(
'createAttachmentLoader: loadAttachmentData is required'
);
throw new TypeError('createAttachmentLoader: loadAttachmentData is required');
}
return async message =>
Object.assign({}, message, {
attachments: await Promise.all(
message.attachments.map(loadAttachmentData)
),
attachments: await Promise.all(message.attachments.map(loadAttachmentData)),
});
};
@ -528,15 +489,11 @@ exports.loadPreviewData = loadAttachmentData => {
exports.deleteAllExternalFiles = ({ deleteAttachmentData, deleteOnDisk }) => {
if (!isFunction(deleteAttachmentData)) {
throw new TypeError(
'deleteAllExternalFiles: deleteAttachmentData must be a function'
);
throw new TypeError('deleteAllExternalFiles: deleteAttachmentData must be a function');
}
if (!isFunction(deleteOnDisk)) {
throw new TypeError(
'deleteAllExternalFiles: deleteOnDisk must be a function'
);
throw new TypeError('deleteAllExternalFiles: deleteOnDisk must be a function');
}
return async message => {
@ -590,10 +547,7 @@ exports.deleteAllExternalFiles = ({ deleteAttachmentData, deleteOnDisk }) => {
// createAttachmentDataWriter :: (RelativePath -> IO Unit)
// Message ->
// IO (Promise Message)
exports.createAttachmentDataWriter = ({
writeExistingAttachmentData,
logger,
}) => {
exports.createAttachmentDataWriter = ({ writeExistingAttachmentData, logger }) => {
if (!isFunction(writeExistingAttachmentData)) {
throw new TypeError(
'createAttachmentDataWriter: writeExistingAttachmentData must be a function'
@ -633,15 +587,11 @@ exports.createAttachmentDataWriter = ({
(attachments || []).forEach(attachment => {
if (!Attachment.hasData(attachment)) {
throw new TypeError(
"'attachment.data' is required during message import"
);
throw new TypeError("'attachment.data' is required during message import");
}
if (!isString(attachment.path)) {
throw new TypeError(
"'attachment.path' is required during message import"
);
throw new TypeError("'attachment.path' is required during message import");
}
});

@ -26,20 +26,12 @@ exports.getImageDimensions = ({ objectUrl, logger }) =>
reject(error);
});
// TODO image/jpeg is hard coded, but it does not look to cause any issues
DecryptedAttachmentsManager.getDecryptedMediaUrl(
objectUrl,
'image/jpg'
).then(decryptedUrl => {
DecryptedAttachmentsManager.getDecryptedMediaUrl(objectUrl, 'image/jpg').then(decryptedUrl => {
image.src = decryptedUrl;
});
});
exports.makeImageThumbnail = ({
size,
objectUrl,
contentType = 'image/png',
logger,
}) =>
exports.makeImageThumbnail = ({ size, objectUrl, contentType = 'image/png', logger }) =>
new Promise((resolve, reject) => {
const image = document.createElement('img');
@ -76,19 +68,12 @@ exports.makeImageThumbnail = ({
reject(error);
});
DecryptedAttachmentsManager.getDecryptedMediaUrl(
objectUrl,
contentType
).then(decryptedUrl => {
DecryptedAttachmentsManager.getDecryptedMediaUrl(objectUrl, contentType).then(decryptedUrl => {
image.src = decryptedUrl;
});
});
exports.makeVideoScreenshot = ({
objectUrl,
contentType = 'image/png',
logger,
}) =>
exports.makeVideoScreenshot = ({ objectUrl, contentType = 'image/png', logger }) =>
new Promise((resolve, reject) => {
const video = document.createElement('video');
@ -96,9 +81,7 @@ exports.makeVideoScreenshot = ({
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas
.getContext('2d')
.drawImage(video, 0, 0, canvas.width, canvas.height);
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
const image = dataURLToBlobSync(canvas.toDataURL(contentType));
@ -114,10 +97,7 @@ exports.makeVideoScreenshot = ({
reject(error);
});
DecryptedAttachmentsManager.getDecryptedMediaUrl(
objectUrl,
contentType
).then(decryptedUrl => {
DecryptedAttachmentsManager.getDecryptedMediaUrl(objectUrl, contentType).then(decryptedUrl => {
video.src = decryptedUrl;
video.muted = true;
// for some reason, this is to be started, otherwise the generated thumbnail will be empty

@ -36,9 +36,7 @@ class WorkerInterface {
if (errorForDisplay) {
return reject(
new Error(
`Error received from worker job ${jobId} (${fnName}): ${errorForDisplay}`
)
new Error(`Error received from worker job ${jobId} (${fnName}): ${errorForDisplay}`)
);
}
@ -72,18 +70,14 @@ class WorkerInterface {
this._removeJob(id);
const end = Date.now();
if (this._DEBUG) {
window.log.info(
`Worker job ${id} (${fnName}) succeeded in ${end - start}ms`
);
window.log.info(`Worker job ${id} (${fnName}) succeeded in ${end - start}ms`);
}
return resolve(value);
},
reject: error => {
this._removeJob(id);
const end = Date.now();
window.log.info(
`Worker job ${id} (${fnName}) failed in ${end - start}ms`
);
window.log.info(`Worker job ${id} (${fnName}) failed in ${end - start}ms`);
return reject(error);
},
};
@ -114,10 +108,7 @@ class WorkerInterface {
});
setTimeout(
() =>
reject(
new TimedOutError(`Worker job ${jobId} (${fnName}) timed out`)
),
() => reject(new TimedOutError(`Worker job ${jobId} (${fnName}) timed out`)),
this.timeout
);
});

@ -57,8 +57,7 @@
const { isEnabled } = this;
const isAppFocused = isFocused();
const isAudioNotificationEnabled =
storage.get('audio-notification') || false;
const isAudioNotificationEnabled = storage.get('audio-notification') || false;
const isAudioNotificationSupported = Settings.isAudioNotificationSupported();
// const isNotificationGroupingSupported = Settings.isNotificationGroupingSupported();
const numNotifications = this.length;
@ -99,9 +98,7 @@
// e.g. Russian:
// http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html
const newMessageCountLabel = `${messagesNotificationCount} ${
messagesNotificationCount === 1
? i18n('newMessage')
: i18n('newMessages')
messagesNotificationCount === 1 ? i18n('newMessage') : i18n('newMessages')
}`;
const last = this.last().toJSON();
@ -141,14 +138,11 @@
iconUrl = last.iconUrl;
break;
default:
window.log.error(
`Error: Unknown user notification setting: '${userSetting}'`
);
window.log.error(`Error: Unknown user notification setting: '${userSetting}'`);
break;
}
const shouldHideExpiringMessageBody =
last.isExpiringMessage && Signal.OS.isMacOS();
const shouldHideExpiringMessageBody = last.isExpiringMessage && Signal.OS.isMacOS();
if (shouldHideExpiringMessageBody) {
message = i18n('newMessage');
}
@ -160,8 +154,7 @@
icon: iconUrl,
silent: !status.shouldPlayNotificationSound,
});
this.lastNotification.onclick = () =>
this.trigger('click', last.conversationId, last.id);
this.lastNotification.onclick = () => this.trigger('click', last.conversationId, last.id);
// We continue to build up more and more messages for our notifications
// until the user comes back to our app or closes the app. Then well

@ -61,14 +61,9 @@
},
async onReceipt(receipt) {
try {
const messages = await window.Signal.Data.getMessagesBySentAt(
receipt.get('timestamp')
);
const messages = await window.Signal.Data.getMessagesBySentAt(receipt.get('timestamp'));
const message = await this.getTargetMessage(
receipt.get('reader'),
messages
);
const message = await this.getTargetMessage(receipt.get('reader'), messages);
if (!message) {
window.log.info(
@ -80,9 +75,7 @@
}
const readBy = message.get('read_by') || [];
const expirationStartTimestamp = message.get(
'expirationStartTimestamp'
);
const expirationStartTimestamp = message.get('expirationStartTimestamp');
readBy.push(receipt.get('reader'));
message.set({
@ -99,9 +92,7 @@
}
// notify frontend listeners
const conversation = window
.getConversationController()
.get(message.get('conversationId'));
const conversation = window.getConversationController().get(message.get('conversationId'));
if (conversation) {
conversation.updateLastMessage();
}

@ -27,20 +27,15 @@
},
async onReceipt(receipt) {
try {
const messages = await window.Signal.Data.getMessagesBySentAt(
receipt.get('timestamp')
);
const messages = await window.Signal.Data.getMessagesBySentAt(receipt.get('timestamp'));
const found = messages.find(
item =>
item.isIncoming() && item.get('source') === receipt.get('sender')
item => item.isIncoming() && item.get('source') === receipt.get('sender')
);
const notificationForMessage = found
? Whisper.Notifications.findWhere({ messageId: found.id })
: null;
const removedNotification = Whisper.Notifications.remove(
notificationForMessage
);
const removedNotification = Whisper.Notifications.remove(notificationForMessage);
const receiptSender = receipt.get('sender');
const receiptTimestamp = receipt.get('timestamp');
const wasMessageFound = Boolean(found);
@ -89,10 +84,7 @@
this.remove(receipt);
} catch (error) {
window.log.error(
'ReadSyncs.onReceipt error:',
error && error.stack ? error.stack : error
);
window.log.error('ReadSyncs.onReceipt error:', error && error.stack ? error.stack : error);
}
},
}))();

@ -46,13 +46,5 @@ function calcPoW(
increment = 1,
startNonce = 0
) {
return pow.calcPoW(
timestamp,
ttl,
pubKey,
data,
difficulty,
increment,
startNonce
);
return pow.calcPoW(timestamp, ttl, pubKey, data, difficulty, increment, startNonce);
}

@ -145,8 +145,7 @@
},
getThemeObject() {
const themeSettings = storage.get('theme-setting') || 'light';
const theme =
themeSettings === 'light' ? window.lightTheme : window.darkTheme;
const theme = themeSettings === 'light' ? window.lightTheme : window.darkTheme;
return theme;
},
showUpdateGroupNameDialog(groupConvo) {
@ -165,9 +164,7 @@
},
showLeaveGroupDialog(groupConvo) {
if (!groupConvo.isGroup()) {
throw new Error(
'showLeaveGroupDialog() called with a non group convo.'
);
throw new Error('showLeaveGroupDialog() called with a non group convo.');
}
const title = i18n('leaveGroup');

@ -67,10 +67,7 @@
.focus()
.select();
} catch (error) {
window.log.error(
'DebugLogView error:',
error && error.stack ? error.stack : error
);
window.log.error('DebugLogView error:', error && error.stack ? error.stack : error);
this.$('.loading').removeClass('loading');
this.$('.result').text(i18n('debugLogError'));
}

@ -140,10 +140,7 @@
Whisper.Import.reset()
)
.then(() =>
Promise.all([
Whisper.Import.start(),
window.Signal.Backup.importFromDirectory(directory),
])
Promise.all([Whisper.Import.start(), window.Signal.Backup.importFromDirectory(directory)])
)
.then(results => {
const importResult = results[1];
@ -158,10 +155,7 @@
return this.finishLightImport(directory);
})
.catch(error => {
window.log.error(
'Error importing:',
error && error.stack ? error.stack : error
);
window.log.error('Error importing:', error && error.stack ? error.stack : error);
this.error = error || new Error('Something went wrong!');
this.state = null;
@ -177,10 +171,7 @@
.getConversationController()
.load()
.then(() =>
Promise.all([
Whisper.Import.saveLocation(directory),
Whisper.Import.complete(),
])
Promise.all([Whisper.Import.saveLocation(directory), Whisper.Import.complete()])
)
.then(() => {
this.state = State.LIGHT_COMPLETE;

@ -15,12 +15,7 @@
const convos = window.getConversationController().getConversations();
this.contacts = convos.filter(
d =>
!!d &&
!d.isBlocked() &&
d.isPrivate() &&
!d.isMe() &&
!!d.get('active_at')
d => !!d && !d.isBlocked() && d.isPrivate() && !d.isMe() && !!d.get('active_at')
);
if (!convo.isPublic()) {
const members = convo.get('members') || [];
@ -93,19 +88,13 @@
return;
}
const allMembers = window.Lodash.concat(existingMembers, newMembers, [
ourPK,
]);
const allMembers = window.Lodash.concat(existingMembers, newMembers, [ourPK]);
const uniqMembers = _.uniq(allMembers, true, d => d);
const groupId = this.convo.get('id');
const groupName = this.convo.get('name');
window.libsession.ClosedGroup.initiateGroupUpdate(
groupId,
groupName,
uniqMembers
);
window.libsession.ClosedGroup.initiateGroupUpdate(groupId, groupName, uniqMembers);
}
}
},

@ -40,9 +40,7 @@
},
update(props) {
const updatedProps = this.augmentProps(props);
const reactElement = this.JSX
? this.JSX
: React.createElement(this.Component, updatedProps);
const reactElement = this.JSX ? this.JSX : React.createElement(this.Component, updatedProps);
ReactDOM.render(reactElement, this.el, () => {
if (this.hasRendered) {
return;

@ -31,9 +31,7 @@
this.titleText = i18n('updateGroupDialogTitle', this.groupName);
// I'd much prefer to integrate mods with groupAdmins
// but lets discuss first...
this.isAdmin = groupConvo.isAdmin(
window.storage.get('primaryDevicePubKey')
);
this.isAdmin = groupConvo.isAdmin(window.storage.get('primaryDevicePubKey'));
}
this.$el.focus();
@ -92,9 +90,7 @@
this.titleText = i18n('updateGroupDialogTitle', this.groupName);
// I'd much prefer to integrate mods with groupAdmins
// but lets discuss first...
this.isAdmin = groupConvo.isAdmin(
window.storage.get('primaryDevicePubKey')
);
this.isAdmin = groupConvo.isAdmin(window.storage.get('primaryDevicePubKey'));
// zero out contactList for now
this.contactsAndMembers = [];
this.existingMembers = [];
@ -116,11 +112,7 @@
this.contactsAndMembers = convos.filter(
d => this.existingMembers.includes(d.id) && d.isPrivate() && !d.isMe()
);
this.contactsAndMembers = _.uniq(
this.contactsAndMembers,
true,
d => d.id
);
this.contactsAndMembers = _.uniq(this.contactsAndMembers, true, d => d.id);
// at least make sure it's an array
if (!Array.isArray(this.existingMembers)) {
@ -160,24 +152,16 @@
const allMembers = window.Lodash.concat(newMembers, [ourPK]);
// We need to NOT trigger an group update if the list of member is the same.
const notPresentInOld = allMembers.filter(
m => !this.existingMembers.includes(m)
);
const notPresentInOld = allMembers.filter(m => !this.existingMembers.includes(m));
const membersToRemove = this.existingMembers.filter(
m => !allMembers.includes(m)
);
const membersToRemove = this.existingMembers.filter(m => !allMembers.includes(m));
// If any extra devices of removed exist in newMembers, ensure that you filter them
const filteredMemberes = allMembers.filter(
member => !_.includes(membersToRemove, member)
);
const filteredMemberes = allMembers.filter(member => !_.includes(membersToRemove, member));
const xor = _.xor(membersToRemove, notPresentInOld);
if (xor.length === 0) {
window.log.info(
'skipping group update: no detected changes in group member list'
);
window.log.info('skipping group update: no detected changes in group member list');
return;
}

@ -8,14 +8,7 @@
Whisper.UserDetailsDialogView = Whisper.View.extend({
className: 'loki-dialog modal',
initialize({
profileName,
avatarPath,
pubkey,
onOk,
onStartConversation,
theme,
}) {
initialize({ profileName, avatarPath, pubkey, onOk, onStartConversation, theme }) {
this.close = this.close.bind(this);
this.profileName = profileName;

@ -1,14 +1,8 @@
export interface CryptoInterface {
DHDecrypt: any;
DHEncrypt: any;
DecryptAESGCM: (
symmetricKey: ArrayBuffer,
ivAndCiphertext: ArrayBuffer
) => Promise<ArrayBuffer>; // AES-GCM
deriveSymmetricKey: (
pubkey: ArrayBuffer,
seckey: ArrayBuffer
) => Promise<ArrayBuffer>;
DecryptAESGCM: (symmetricKey: ArrayBuffer, ivAndCiphertext: ArrayBuffer) => Promise<ArrayBuffer>; // AES-GCM
deriveSymmetricKey: (pubkey: ArrayBuffer, seckey: ArrayBuffer) => Promise<ArrayBuffer>;
EncryptAESGCM: any; // AES-GCM
_decodeSnodeAddressToPubKey: any;
decryptToken: any;

@ -19,14 +19,8 @@
async function DHEncrypt(symmetricKey, plainText) {
const iv = libsignal.crypto.getRandomBytes(IV_LENGTH);
const ciphertext = await libsignal.crypto.encrypt(
symmetricKey,
plainText,
iv
);
const ivAndCiphertext = new Uint8Array(
iv.byteLength + ciphertext.byteLength
);
const ciphertext = await libsignal.crypto.encrypt(symmetricKey, plainText, iv);
const ivAndCiphertext = new Uint8Array(iv.byteLength + ciphertext.byteLength);
ivAndCiphertext.set(new Uint8Array(iv));
ivAndCiphertext.set(new Uint8Array(ciphertext), iv.byteLength);
return ivAndCiphertext;
@ -71,13 +65,9 @@
async function EncryptAESGCM(symmetricKey, plaintext) {
const nonce = crypto.getRandomValues(new Uint8Array(NONCE_LENGTH));
const key = await crypto.subtle.importKey(
'raw',
symmetricKey,
{ name: 'AES-GCM' },
false,
['encrypt']
);
const key = await crypto.subtle.importKey('raw', symmetricKey, { name: 'AES-GCM' }, false, [
'encrypt',
]);
const ciphertext = await crypto.subtle.encrypt(
{ name: 'AES-GCM', iv: nonce, tagLength: 128 },
@ -85,9 +75,7 @@
plaintext
);
const ivAndCiphertext = new Uint8Array(
NONCE_LENGTH + ciphertext.byteLength
);
const ivAndCiphertext = new Uint8Array(NONCE_LENGTH + ciphertext.byteLength);
ivAndCiphertext.set(nonce);
ivAndCiphertext.set(new Uint8Array(ciphertext), nonce.byteLength);
@ -99,19 +87,11 @@
const nonce = ivAndCiphertext.slice(0, NONCE_LENGTH);
const ciphertext = ivAndCiphertext.slice(NONCE_LENGTH);
const key = await crypto.subtle.importKey(
'raw',
symmetricKey,
{ name: 'AES-GCM' },
false,
['decrypt']
);
const key = await crypto.subtle.importKey('raw', symmetricKey, { name: 'AES-GCM' }, false, [
'decrypt',
]);
return crypto.subtle.decrypt(
{ name: 'AES-GCM', iv: nonce },
key,
ciphertext
);
return crypto.subtle.decrypt({ name: 'AES-GCM', iv: nonce }, key, ciphertext);
}
async function DHDecrypt(symmetricKey, ivAndCiphertext) {
@ -152,10 +132,7 @@
throw new Error('Failed to get keypair for token decryption');
}
const { privKey } = keyPair;
const symmetricKey = await libsignal.Curve.async.calculateAgreement(
serverPubKey,
privKey
);
const symmetricKey = await libsignal.Curve.async.calculateAgreement(serverPubKey, privKey);
const token = await DHDecrypt(symmetricKey, ivAndCiphertext);

@ -62,15 +62,7 @@ const pow = {
},
// Return nonce that hashes together with payload lower than the target
async calcPoW(
timestamp,
ttl,
pubKey,
data,
_difficulty = null,
increment = 1,
startNonce = 0
) {
async calcPoW(timestamp, ttl, pubKey, data, _difficulty = null, increment = 1, startNonce = 0) {
const payload = new Uint8Array(
dcodeIO.ByteBuffer.wrap(
timestamp.toString() + ttl.toString() + pubKey + data,
@ -84,9 +76,7 @@ const pow = {
let nonce = new Uint8Array(NONCE_LEN);
nonce = pow.incrementNonce(nonce, startNonce); // initial value
let trialValue = new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255]);
const initialHash = new Uint8Array(
await crypto.subtle.digest('SHA-512', payload)
);
const initialHash = new Uint8Array(await crypto.subtle.digest('SHA-512', payload));
const innerPayload = new Uint8Array(initialHash.length + NONCE_LEN);
innerPayload.set(initialHash, NONCE_LEN);
let resultHash;
@ -97,9 +87,10 @@ const pow = {
innerPayload.set(nonce);
// eslint-disable-next-line no-await-in-loop
resultHash = await crypto.subtle.digest('SHA-512', innerPayload);
trialValue = new Uint8Array(
dcodeIO.ByteBuffer.wrap(resultHash, 'hex').toArrayBuffer()
).slice(0, NONCE_LEN);
trialValue = new Uint8Array(dcodeIO.ByteBuffer.wrap(resultHash, 'hex').toArrayBuffer()).slice(
0,
NONCE_LEN
);
}
return pow.bufferToBase64(nonce);
},
@ -121,10 +112,7 @@ const pow = {
// totalLen + innerFrac
const lenPlusInnerFrac = JSBI.add(totalLen, innerFrac);
// difficulty * lenPlusInnerFrac
const denominator = JSBI.multiply(
JSBI.BigInt(difficulty),
lenPlusInnerFrac
);
const denominator = JSBI.multiply(JSBI.BigInt(difficulty), lenPlusInnerFrac);
// 2^64 - 1
const two64 = JSBI.subtract(
JSBI.exponentiate(JSBI.BigInt(2), JSBI.BigInt(64)), // 2^64

@ -5,13 +5,9 @@ let plotlyDiv;
const workers = [];
async function run(messageLength, numWorkers = 1, difficulty = 100, ttl = 72) {
const timestamp = Math.floor(Date.now() / 1000);
const pubKey =
'05ec8635a07a13743516c7c9b3412f3e8252efb7fcaf67eb1615ffba62bebc6802';
const pubKey = '05ec8635a07a13743516c7c9b3412f3e8252efb7fcaf67eb1615ffba62bebc6802';
const message = randomString(messageLength);
const messageBuffer = dcodeIO.ByteBuffer.wrap(
message,
'utf8'
).toArrayBuffer();
const messageBuffer = dcodeIO.ByteBuffer.wrap(message, 'utf8').toArrayBuffer();
const data = dcodeIO.ByteBuffer.wrap(messageBuffer).toString('base64');
const promises = [];
const t0 = performance.now();
@ -48,13 +44,7 @@ async function run(messageLength, numWorkers = 1, difficulty = 100, ttl = 72) {
workers.forEach(worker => worker.terminate());
}
async function runPoW({
iteration,
difficulty,
numWorkers,
messageLength = 50,
ttl = 72,
}) {
async function runPoW({ iteration, difficulty, numWorkers, messageLength = 50, ttl = 72 }) {
const name = `W:${numWorkers} - NT: ${difficulty} - L:${messageLength} - TTL:${ttl}`;
Plotly.addTraces(plotlyDiv, {
y: [],
@ -74,8 +64,7 @@ async function runPoW({
function randomString(length) {
let text = '';
const possible =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < length; i += 1) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
@ -89,21 +78,11 @@ async function startMessageLengthRun() {
const iteration0 = parseFloat(document.getElementById('iteration0').value);
const difficulty0 = parseFloat(document.getElementById('difficulty0').value);
const numWorkers0 = parseFloat(document.getElementById('numWorkers0').value);
const messageLengthStart0 = parseFloat(
document.getElementById('messageLengthStart0').value
);
const messageLengthStop0 = parseFloat(
document.getElementById('messageLengthStop0').value
);
const messageLengthStep0 = parseFloat(
document.getElementById('messageLengthStep0').value
);
const messageLengthStart0 = parseFloat(document.getElementById('messageLengthStart0').value);
const messageLengthStop0 = parseFloat(document.getElementById('messageLengthStop0').value);
const messageLengthStep0 = parseFloat(document.getElementById('messageLengthStep0').value);
const TTL0 = parseFloat(document.getElementById('TTL0').value);
for (
let l = messageLengthStart0;
l < messageLengthStop0;
l += messageLengthStep0
) {
for (let l = messageLengthStart0; l < messageLengthStop0; l += messageLengthStep0) {
// eslint-disable-next-line no-await-in-loop
await runPoW({
iteration: iteration0,
@ -117,21 +96,11 @@ async function startMessageLengthRun() {
async function startNumWorkerRun() {
const iteration1 = parseFloat(document.getElementById('iteration1').value);
const difficulty1 = parseFloat(document.getElementById('difficulty1').value);
const numWorkersStart1 = parseFloat(
document.getElementById('numWorkersStart1').value
);
const numWorkersEnd1 = parseFloat(
document.getElementById('numWorkersEnd1').value
);
const messageLength1 = parseFloat(
document.getElementById('messageLength1').value
);
const numWorkersStart1 = parseFloat(document.getElementById('numWorkersStart1').value);
const numWorkersEnd1 = parseFloat(document.getElementById('numWorkersEnd1').value);
const messageLength1 = parseFloat(document.getElementById('messageLength1').value);
const TTL1 = parseFloat(document.getElementById('TTL1').value);
for (
let numWorkers = numWorkersStart1;
numWorkers <= numWorkersEnd1;
numWorkers += 1
) {
for (let numWorkers = numWorkersStart1; numWorkers <= numWorkersEnd1; numWorkers += 1) {
// eslint-disable-next-line no-await-in-loop
await runPoW({
iteration: iteration1,
@ -144,19 +113,11 @@ async function startNumWorkerRun() {
}
async function startDifficultyRun() {
const iteration2 = parseFloat(document.getElementById('iteration2').value);
const messageLength2 = parseFloat(
document.getElementById('messageLength2').value
);
const messageLength2 = parseFloat(document.getElementById('messageLength2').value);
const numWorkers2 = parseFloat(document.getElementById('numWorkers2').value);
const difficultyStart2 = parseFloat(
document.getElementById('difficultyStart2').value
);
const difficultyStop2 = parseFloat(
document.getElementById('difficultyStop2').value
);
const difficultyStep2 = parseFloat(
document.getElementById('difficultyStep2').value
);
const difficultyStart2 = parseFloat(document.getElementById('difficultyStart2').value);
const difficultyStop2 = parseFloat(document.getElementById('difficultyStop2').value);
const difficultyStep2 = parseFloat(document.getElementById('difficultyStep2').value);
const TTL2 = parseFloat(document.getElementById('TTL2').value);
for (let n = difficultyStart2; n < difficultyStop2; n += difficultyStep2) {
// eslint-disable-next-line no-await-in-loop
@ -172,9 +133,7 @@ async function startDifficultyRun() {
async function starTTLRun() {
const iteration3 = parseFloat(document.getElementById('iteration3').value);
const difficulty3 = parseFloat(document.getElementById('difficulty3').value);
const messageLength3 = parseFloat(
document.getElementById('messageLength3').value
);
const messageLength3 = parseFloat(document.getElementById('messageLength3').value);
const numWorkers3 = parseFloat(document.getElementById('numWorkers3').value);
const TTLStart3 = parseFloat(document.getElementById('TTLStart3').value);
const TTLStop3 = parseFloat(document.getElementById('TTLStop3').value);

@ -1,12 +1,6 @@
/* global assert, JSBI, pow */
const {
calcTarget,
incrementNonce,
bufferToBase64,
bigIntToUint8Array,
greaterThan,
} = pow;
const { calcTarget, incrementNonce, bufferToBase64, bigIntToUint8Array, greaterThan } = pow;
describe('Proof of Work', () => {
describe('#incrementNonce', () => {
@ -25,20 +19,14 @@ describe('Proof of Work', () => {
it('should increment a Uint8Array nonce correctly in a loop', () => {
let arr = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]);
assert.deepEqual(
incrementNonce(arr),
new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1])
);
assert.deepEqual(incrementNonce(arr), new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]));
arr = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]);
for (let i = 0; i <= 255; i += 1) {
arr = incrementNonce(arr);
}
assert.deepEqual(arr, new Uint8Array([0, 0, 0, 0, 0, 0, 1, 0]));
arr = new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255]);
assert.deepEqual(
incrementNonce(arr),
new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0])
);
assert.deepEqual(incrementNonce(arr), new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]));
});
});

@ -43,14 +43,8 @@
const iv = encryptedBin.slice(0, 16);
const ciphertext = encryptedBin.slice(16, encryptedBin.byteLength - 32);
const ivAndCiphertext = encryptedBin.slice(
0,
encryptedBin.byteLength - 32
);
const mac = encryptedBin.slice(
encryptedBin.byteLength - 32,
encryptedBin.byteLength
);
const ivAndCiphertext = encryptedBin.slice(0, encryptedBin.byteLength - 32);
const mac = encryptedBin.slice(encryptedBin.byteLength - 32, encryptedBin.byteLength);
return verifyMAC(ivAndCiphertext, macKey, mac, 32)
.then(() => {
@ -63,10 +57,7 @@
},
encryptAttachment(plaintext, keys, iv) {
if (
!(plaintext instanceof ArrayBuffer) &&
!ArrayBuffer.isView(plaintext)
) {
if (!(plaintext instanceof ArrayBuffer) && !ArrayBuffer.isView(plaintext)) {
throw new TypeError(
`\`plaintext\` must be an \`ArrayBuffer\` or \`ArrayBufferView\`; got: ${typeof plaintext}`
);
@ -109,20 +100,11 @@
.importKey('raw', key, { name: 'AES-GCM' }, false, ['encrypt'])
.then(keyForEncryption =>
crypto.subtle
.encrypt(
{ name: 'AES-GCM', iv, tagLength: PROFILE_TAG_LENGTH },
keyForEncryption,
data
)
.encrypt({ name: 'AES-GCM', iv, tagLength: PROFILE_TAG_LENGTH }, keyForEncryption, data)
.then(ciphertext => {
const ivAndCiphertext = new Uint8Array(
PROFILE_IV_LENGTH + ciphertext.byteLength
);
const ivAndCiphertext = new Uint8Array(PROFILE_IV_LENGTH + ciphertext.byteLength);
ivAndCiphertext.set(new Uint8Array(iv));
ivAndCiphertext.set(
new Uint8Array(ciphertext),
PROFILE_IV_LENGTH
);
ivAndCiphertext.set(new Uint8Array(ciphertext), PROFILE_IV_LENGTH);
return ivAndCiphertext.buffer;
})
);
@ -166,10 +148,7 @@
return textsecure.crypto.encryptProfile(padded.buffer, key);
},
decryptProfileName(encryptedProfileName, key) {
const data = dcodeIO.ByteBuffer.wrap(
encryptedProfileName,
'base64'
).toArrayBuffer();
const data = dcodeIO.ByteBuffer.wrap(encryptedProfileName, 'base64').toArrayBuffer();
return textsecure.crypto.decryptProfile(data, key).then(decrypted => {
// unpad
const padded = new Uint8Array(decrypted);

@ -44,8 +44,7 @@ function getStringable(thing) {
window.textsecure.utils = (() => {
const self = {};
self.unencodeNumber = number => number.split('.');
self.isNumberSane = number =>
number[0] === '+' && /^[0-9]+$/.test(number.substring(1));
self.isNumberSane = number => number[0] === '+' && /^[0-9]+$/.test(number.substring(1));
/** ************************
*** JSON'ing Utilities ***

@ -27,11 +27,7 @@ interface CurveSync {
generateKeyPair(): KeyPair;
createKeyPair(privKey: ArrayBuffer): KeyPair;
calculateAgreement(pubKey: ArrayBuffer, privKey: ArrayBuffer): ArrayBuffer;
verifySignature(
pubKey: ArrayBuffer,
msg: ArrayBuffer,
sig: ArrayBuffer
): void;
verifySignature(pubKey: ArrayBuffer, msg: ArrayBuffer, sig: ArrayBuffer): void;
calculateSignature(privKey: ArrayBuffer, message: ArrayBuffer): ArrayBuffer;
validatePubKeyFormat(pubKey: ArrayBuffer): ArrayBuffer;
}
@ -39,19 +35,9 @@ interface CurveSync {
interface CurveAsync {
generateKeyPair(): Promise<KeyPair>;
createKeyPair(privKey: ArrayBuffer): Promise<KeyPair>;
calculateAgreement(
pubKey: ArrayBuffer,
privKey: ArrayBuffer
): Promise<ArrayBuffer>;
verifySignature(
pubKey: ArrayBuffer,
msg: ArrayBuffer,
sig: ArrayBuffer
): Promise<void>;
calculateSignature(
privKey: ArrayBuffer,
message: ArrayBuffer
): Promise<ArrayBuffer>;
calculateAgreement(pubKey: ArrayBuffer, privKey: ArrayBuffer): Promise<ArrayBuffer>;
verifySignature(pubKey: ArrayBuffer, msg: ArrayBuffer, sig: ArrayBuffer): Promise<void>;
calculateSignature(privKey: ArrayBuffer, message: ArrayBuffer): Promise<ArrayBuffer>;
validatePubKeyFormat(pubKey: ArrayBuffer): Promise<ArrayBuffer>;
}
@ -60,23 +46,10 @@ export interface CurveInterface extends CurveSync {
}
export interface CryptoInterface {
encrypt(
key: ArrayBuffer,
data: ArrayBuffer,
iv: ArrayBuffer
): Promise<ArrayBuffer>;
decrypt(
key: ArrayBuffer,
data: ArrayBuffer,
iv: ArrayBuffer
): Promise<ArrayBuffer>;
encrypt(key: ArrayBuffer, data: ArrayBuffer, iv: ArrayBuffer): Promise<ArrayBuffer>;
decrypt(key: ArrayBuffer, data: ArrayBuffer, iv: ArrayBuffer): Promise<ArrayBuffer>;
calculateMAC(key: ArrayBuffer, data: ArrayBuffer): Promise<ArrayBuffer>;
verifyMAC(
data: ArrayBuffer,
key: ArrayBuffer,
mac: ArrayBuffer,
length: number
): Promise<void>;
verifyMAC(data: ArrayBuffer, key: ArrayBuffer, mac: ArrayBuffer, length: number): Promise<void>;
getRandomBytes(size: number): ArrayBuffer;
}

@ -80,12 +80,8 @@ window.textsecure = window.textsecure || {};
textsecure.MessageReceiver = function MessageReceiverWrapper() {
const messageReceiver = new MessageReceiver();
this.addEventListener = messageReceiver.addEventListener.bind(
messageReceiver
);
this.removeEventListener = messageReceiver.removeEventListener.bind(
messageReceiver
);
this.addEventListener = messageReceiver.addEventListener.bind(messageReceiver);
this.removeEventListener = messageReceiver.removeEventListener.bind(messageReceiver);
this.close = messageReceiver.close.bind(messageReceiver);
this.stopProcessing = messageReceiver.stopProcessing.bind(messageReceiver);
@ -97,9 +93,6 @@ textsecure.MessageReceiver.prototype = {
constructor: textsecure.MessageReceiver,
};
textsecure.MessageReceiver.stringToArrayBuffer =
MessageReceiver.stringToArrayBuffer;
textsecure.MessageReceiver.arrayBufferToString =
MessageReceiver.arrayBufferToString;
textsecure.MessageReceiver.arrayBufferToStringBase64 =
MessageReceiver.arrayBufferToStringBase64;
textsecure.MessageReceiver.stringToArrayBuffer = MessageReceiver.stringToArrayBuffer;
textsecure.MessageReceiver.arrayBufferToString = MessageReceiver.arrayBufferToString;
textsecure.MessageReceiver.arrayBufferToStringBase64 = MessageReceiver.arrayBufferToStringBase64;

@ -10,9 +10,9 @@
{ root: window.PROTO_ROOT, file: filename },
(error, result) => {
if (error) {
const text = `Error loading protos from ${filename} (root: ${
window.PROTO_ROOT
}) ${error && error.stack ? error.stack : error}`;
const text = `Error loading protos from ${filename} (root: ${window.PROTO_ROOT}) ${
error && error.stack ? error.stack : error
}`;
window.log.error(text);
throw error;
}

@ -33,8 +33,7 @@
},
};
window.textsecure.storage.put = (key, value) =>
textsecure.storage.impl.put(key, value);
window.textsecure.storage.put = (key, value) => textsecure.storage.impl.put(key, value);
window.textsecure.storage.get = (key, defaultValue) =>
textsecure.storage.impl.get(key, defaultValue);
window.textsecure.storage.remove = key => textsecure.storage.impl.remove(key);

@ -41,10 +41,7 @@
},
setLastProfileUpdateTimestamp(lastUpdateTimestamp) {
textsecure.storage.put(
'last_profile_update_timestamp',
lastUpdateTimestamp
);
textsecure.storage.put('last_profile_update_timestamp', lastUpdateTimestamp);
},
getDeviceId() {

@ -38,20 +38,11 @@
let nMod3;
let nMod4;
for (
let nUint24 = 0, nOutIdx = 0, nInIdx = 0;
nInIdx < nInLen;
nInIdx += 1
) {
for (let nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx += 1) {
nMod4 = nInIdx & 3;
nUint24 |=
StringView.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (18 - 6 * nMod4);
nUint24 |= StringView.b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (18 - 6 * nMod4);
if (nMod4 === 3 || nInLen - nInIdx === 1) {
for (
nMod3 = 0;
nMod3 < 3 && nOutIdx < nOutLen;
nMod3 += 1, nOutIdx += 1
) {
for (nMod3 = 0; nMod3 < 3 && nOutIdx < nOutLen; nMod3 += 1, nOutIdx += 1) {
taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255;
}
nUint24 = 0;
@ -77,11 +68,7 @@
bytesToBase64(aBytes) {
let nMod3;
let sB64Enc = '';
for (
let nLen = aBytes.length, nUint24 = 0, nIdx = 0;
nIdx < nLen;
nIdx += 1
) {
for (let nLen = aBytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx += 1) {
nMod3 = nIdx % 3;
if (nIdx > 0 && ((nIdx * 4) / 3) % 76 === 0) {
sB64Enc += '\r\n';
@ -102,16 +89,12 @@
arrayBufferToHex(aArrayBuffer) {
return Array.prototype.map
.call(new Uint8Array(aArrayBuffer), x =>
`00${x.toString(16)}`.slice(-2)
)
.call(new Uint8Array(aArrayBuffer), x => `00${x.toString(16)}`.slice(-2))
.join('');
},
hexToArrayBuffer(aString) {
return new Uint8Array(
aString.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16))
).buffer;
return new Uint8Array(aString.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16))).buffer;
},
};
})();

@ -15,8 +15,7 @@
let complete = false;
let timer = setTimeout(() => {
if (!complete) {
const message = `${id ||
''} task did not complete in time. Calling stack: ${
const message = `${id || ''} task did not complete in time. Calling stack: ${
errorForStack.stack
}`;

@ -8,70 +8,48 @@ describe('encrypting and decrypting profile data', () => {
const buffer = dcodeIO.ByteBuffer.wrap(name).toArrayBuffer();
const key = libsignal.crypto.getRandomBytes(32);
return textsecure.crypto
.encryptProfileName(buffer, key)
.then(encrypted => {
assert(encrypted.byteLength === NAME_PADDED_LENGTH + 16 + 12);
return textsecure.crypto
.decryptProfileName(encrypted, key)
.then(decrypted => {
assert.strictEqual(
dcodeIO.ByteBuffer.wrap(decrypted).toString('utf8'),
'Alice'
);
});
return textsecure.crypto.encryptProfileName(buffer, key).then(encrypted => {
assert(encrypted.byteLength === NAME_PADDED_LENGTH + 16 + 12);
return textsecure.crypto.decryptProfileName(encrypted, key).then(decrypted => {
assert.strictEqual(dcodeIO.ByteBuffer.wrap(decrypted).toString('utf8'), 'Alice');
});
});
});
it('works for empty string', () => {
const name = dcodeIO.ByteBuffer.wrap('').toArrayBuffer();
const key = libsignal.crypto.getRandomBytes(32);
return textsecure.crypto
.encryptProfileName(name.buffer, key)
.then(encrypted => {
assert(encrypted.byteLength === NAME_PADDED_LENGTH + 16 + 12);
return textsecure.crypto
.decryptProfileName(encrypted, key)
.then(decrypted => {
assert.strictEqual(decrypted.byteLength, 0);
assert.strictEqual(
dcodeIO.ByteBuffer.wrap(decrypted).toString('utf8'),
''
);
});
return textsecure.crypto.encryptProfileName(name.buffer, key).then(encrypted => {
assert(encrypted.byteLength === NAME_PADDED_LENGTH + 16 + 12);
return textsecure.crypto.decryptProfileName(encrypted, key).then(decrypted => {
assert.strictEqual(decrypted.byteLength, 0);
assert.strictEqual(dcodeIO.ByteBuffer.wrap(decrypted).toString('utf8'), '');
});
});
});
});
describe('encrypting and decrypting profile avatars', () => {
it('encrypts and decrypts', () => {
const buffer = dcodeIO.ByteBuffer.wrap(
'This is an avatar'
).toArrayBuffer();
const buffer = dcodeIO.ByteBuffer.wrap('This is an avatar').toArrayBuffer();
const key = libsignal.crypto.getRandomBytes(32);
return textsecure.crypto.encryptProfile(buffer, key).then(encrypted => {
assert(encrypted.byteLength === buffer.byteLength + 16 + 12);
return textsecure.crypto
.decryptProfile(encrypted, key)
.then(decrypted => {
assertEqualArrayBuffers(buffer, decrypted);
});
return textsecure.crypto.decryptProfile(encrypted, key).then(decrypted => {
assertEqualArrayBuffers(buffer, decrypted);
});
});
});
it('throws when decrypting with the wrong key', () => {
const buffer = dcodeIO.ByteBuffer.wrap(
'This is an avatar'
).toArrayBuffer();
const buffer = dcodeIO.ByteBuffer.wrap('This is an avatar').toArrayBuffer();
const key = libsignal.crypto.getRandomBytes(32);
const badKey = libsignal.crypto.getRandomBytes(32);
return textsecure.crypto.encryptProfile(buffer, key).then(encrypted => {
assert(encrypted.byteLength === buffer.byteLength + 16 + 12);
return textsecure.crypto
.decryptProfile(encrypted, badKey)
.catch(error => {
assert.strictEqual(error.name, 'ProfileDecryptError');
});
return textsecure.crypto.decryptProfile(encrypted, badKey).catch(error => {
assert.strictEqual(error.name, 'ProfileDecryptError');
});
});
});
});

@ -46,8 +46,7 @@ function getMainWindow() {
// Tray icon and related objects
let tray = null;
const startInTray = process.argv.some(arg => arg === '--start-in-tray');
const usingTrayIcon =
startInTray || process.argv.some(arg => arg === '--use-tray-icon');
const usingTrayIcon = startInTray || process.argv.some(arg => arg === '--use-tray-icon');
const config = require('./app/config');
@ -56,8 +55,7 @@ const config = require('./app/config');
const userConfig = require('./app/user_config');
const passwordUtil = require('./ts/util/passwordUtils');
const importMode =
process.argv.some(arg => arg === '--import') || config.get('import');
const importMode = process.argv.some(arg => arg === '--import') || config.get('import');
const development = config.environment === 'development';
const appInstance = config.util.getEnv('NODE_APP_INSTANCE') || 0;
@ -76,10 +74,7 @@ const sql = require('./app/sql');
const sqlChannels = require('./app/sql_channel');
const windowState = require('./app/window_state');
const { createTemplate } = require('./app/menu');
const {
installFileHandler,
installWebHandler,
} = require('./app/protocol_filter');
const { installFileHandler, installWebHandler } = require('./app/protocol_filter');
const { installPermissionsHandler } = require('./app/permissions');
const _sodium = require('libsodium-wrappers');
@ -218,10 +213,7 @@ function getWindowSize() {
const { minWidth, minHeight, defaultWidth, defaultHeight } = WINDOW_SIZE;
// Ensure that the screen can fit within the default size
const width = Math.min(defaultWidth, Math.max(minWidth, screenSize.width));
const height = Math.min(
defaultHeight,
Math.max(minHeight, screenSize.height)
);
const height = Math.min(defaultHeight, Math.max(minHeight, screenSize.height));
return { width, height, minWidth, minHeight };
}
@ -234,15 +226,12 @@ function isVisible(window, bounds) {
const BOUNDS_BUFFER = 100;
// requiring BOUNDS_BUFFER pixels on the left or right side
const rightSideClearOfLeftBound =
window.x + window.width >= boundsX + BOUNDS_BUFFER;
const leftSideClearOfRightBound =
window.x <= boundsX + boundsWidth - BOUNDS_BUFFER;
const rightSideClearOfLeftBound = window.x + window.width >= boundsX + BOUNDS_BUFFER;
const leftSideClearOfRightBound = window.x <= boundsX + boundsWidth - BOUNDS_BUFFER;
// top can't be offscreen, and must show at least BOUNDS_BUFFER pixels at bottom
const topClearOfUpperBound = window.y >= boundsY;
const topClearOfLowerBound =
window.y <= boundsY + boundsHeight - BOUNDS_BUFFER;
const topClearOfLowerBound = window.y <= boundsY + boundsHeight - BOUNDS_BUFFER;
return (
rightSideClearOfLeftBound &&
@ -275,14 +264,7 @@ async function createWindow() {
},
icon: path.join(__dirname, 'images', 'session', 'session_icon_64.png'),
},
_.pick(windowConfig, [
'maximized',
'autoHideMenuBar',
'width',
'height',
'x',
'y',
])
_.pick(windowConfig, ['maximized', 'autoHideMenuBar', 'width', 'height', 'x', 'y'])
);
if (!_.isNumber(windowOptions.width) || windowOptions.width < minWidth) {
@ -315,10 +297,7 @@ async function createWindow() {
delete windowOptions.fullscreen;
}
logger.info(
'Initializing BrowserWindow config: %s',
JSON.stringify(windowOptions)
);
logger.info('Initializing BrowserWindow config: %s', JSON.stringify(windowOptions));
// Create the browser window.
mainWindow = new BrowserWindow(windowOptions);
@ -355,10 +334,7 @@ async function createWindow() {
windowConfig.fullscreen = true;
}
logger.info(
'Updating BrowserWindow config: %s',
JSON.stringify(windowConfig)
);
logger.info('Updating BrowserWindow config: %s', JSON.stringify(windowConfig));
ephemeralConfig.set('window', windowConfig);
}
@ -377,13 +353,9 @@ async function createWindow() {
if (config.environment === 'test') {
mainWindow.loadURL(prepareURL([__dirname, 'test', 'index.html']));
} else if (config.environment === 'test-lib') {
mainWindow.loadURL(
prepareURL([__dirname, 'libtextsecure', 'test', 'index.html'])
);
mainWindow.loadURL(prepareURL([__dirname, 'libtextsecure', 'test', 'index.html']));
} else if (config.environment === 'test-loki') {
mainWindow.loadURL(
prepareURL([__dirname, 'libloki', 'test', 'index.html'])
);
mainWindow.loadURL(prepareURL([__dirname, 'libloki', 'test', 'index.html']));
} else if (config.environment.includes('test-integration')) {
mainWindow.loadURL(prepareURL([__dirname, 'background_test.html']));
} else {
@ -422,10 +394,7 @@ async function createWindow() {
// 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 (
!windowState.shouldQuit() &&
(usingTrayIcon || process.platform === 'darwin')
) {
if (!windowState.shouldQuit() && (usingTrayIcon || process.platform === 'darwin')) {
// toggle the visibility of the show/hide tray icon menu entries
if (tray) {
tray.updateContextMenu();
@ -476,10 +445,7 @@ async function readyForUpdates() {
await updater.start(getMainWindow, userConfig, locale.messages, logger);
} catch (error) {
const log = logger || console;
log.error(
'Error starting update checks:',
error && error.stack ? error.stack : error
);
log.error('Error starting update checks:', error && error.stack ? error.stack : error);
}
}
ipc.once('ready-for-updates', readyForUpdates);
@ -555,10 +521,7 @@ function showPasswordWindow() {
// 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 (
!windowState.shouldQuit() &&
(usingTrayIcon || process.platform === 'darwin')
) {
if (!windowState.shouldQuit() && (usingTrayIcon || process.platform === 'darwin')) {
// toggle the visibility of the show/hide tray icon menu entries
if (tray) {
tray.updateContextMenu();
@ -717,9 +680,7 @@ app.on('ready', async () => {
function getDefaultSQLKey() {
let key = userConfig.get('key');
if (!key) {
console.log(
'key/initialize: Generating new encryption key, since we did not find it on disk'
);
console.log('key/initialize: Generating new encryption key, since we did not find it on disk');
// https://www.zetetic.net/sqlcipher/sqlcipher-api/#key
key = crypto.randomBytes(32).toString('hex');
userConfig.set('key', key);
@ -754,9 +715,7 @@ async function showMainWindow(sqlKey, passwordAttempt = false) {
async function cleanupOrphanedAttachments() {
const allAttachments = await attachments.getAllAttachments(userDataPath);
const orphanedAttachments = await sql.removeKnownAttachments(
allAttachments
);
const orphanedAttachments = await sql.removeKnownAttachments(allAttachments);
await attachments.deleteAll({
userDataPath,
attachments: orphanedAttachments,
@ -823,9 +782,7 @@ async function requestShutdown() {
// 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.ts in the browser.
setTimeout(() => {
console.log(
'requestShutdown: Response never received; forcing shutdown.'
);
console.log('requestShutdown: Response never received; forcing shutdown.');
resolve();
}, 2 * 60 * 1000);
});
@ -833,10 +790,7 @@ async function requestShutdown() {
try {
await request;
} catch (error) {
console.log(
'requestShutdown error:',
error && error.stack ? error.stack : error
);
console.log('requestShutdown error:', error && error.stack ? error.stack : error);
}
}
@ -954,8 +908,7 @@ ipc.on('update-tray-icon', (event, unreadCount) => {
// Password screen related IPC calls
ipc.on('password-window-login', async (event, passPhrase) => {
const sendResponse = e =>
event.sender.send('password-window-login-response', e);
const sendResponse = e => event.sender.send('password-window-login-response', e);
try {
const passwordAttempt = true;
@ -977,8 +930,7 @@ ipc.on('set-password', async (event, passPhrase, oldPhrase) => {
if (hash && !hashMatches) {
const incorrectOldPassword = locale.messages.invalidOldPassword.message;
sendResponse(
incorrectOldPassword ||
'Failed to set password: Old password provided is invalid'
incorrectOldPassword || 'Failed to set password: Old password provided is invalid'
);
return;
}

@ -31,9 +31,7 @@ http
// Avoid https://en.wikipedia.org/wiki/Directory_traversal_attack
// e.g curl --path-as-is http://localhost:9000/../fileInDanger.txt
// by limiting the path to current directory only
const sanitizePath = path
.normalize(parsedUrl.pathname)
.replace(/^(\.\.[/\\])+/, '');
const sanitizePath = path.normalize(parsedUrl.pathname).replace(/^(\.\.[/\\])+/, '');
let pathname = path.join(__dirname, sanitizePath);
fs.exists(pathname, exist => {
if (!exist) {

@ -24,9 +24,7 @@ window.getAppInstance = () => config.appInstance;
const electron = require('electron');
const ipc = electron.ipcRenderer;
const {
SessionPasswordPrompt,
} = require('./ts/components/session/SessionPasswordPrompt');
const { SessionPasswordPrompt } = require('./ts/components/session/SessionPasswordPrompt');
window.Signal = {
Components: {

@ -67,10 +67,7 @@ window.lokiFeatureFlags = {
padOutgoingAttachments: false,
};
if (
typeof process.env.NODE_ENV === 'string' &&
process.env.NODE_ENV.includes('test-integration')
) {
if (typeof process.env.NODE_ENV === 'string' && process.env.NODE_ENV.includes('test-integration')) {
window.electronRequire = require;
// during test-integration, file server is started on localhost
window.getDefaultFileServer = () => 'http://127.0.0.1:7070';
@ -100,8 +97,7 @@ window.CONSTANTS = new (function() {
this.LNS_MAX_LENGTH = 64;
// Conforms to naming rules here
// https://loki.network/2020/03/25/loki-name-system-the-facts/
this.LNS_REGEX = `^[a-zA-Z0-9_]([a-zA-Z0-9_-]{0,${this.LNS_MAX_LENGTH -
2}}[a-zA-Z0-9_]){0,1}$`;
this.LNS_REGEX = `^[a-zA-Z0-9_]([a-zA-Z0-9_-]{0,${this.LNS_MAX_LENGTH - 2}}[a-zA-Z0-9_]){0,1}$`;
this.MIN_GUARD_COUNT = 2;
this.DESIRED_GUARD_COUNT = 3;
})();
@ -185,11 +181,9 @@ window.showWindow = () => {
ipc.send('show-window');
};
window.setAutoHideMenuBar = autoHide =>
ipc.send('set-auto-hide-menu-bar', autoHide);
window.setAutoHideMenuBar = autoHide => ipc.send('set-auto-hide-menu-bar', autoHide);
window.setMenuBarVisibility = visibility =>
ipc.send('set-menu-bar-visibility', visibility);
window.setMenuBarVisibility = visibility => ipc.send('set-menu-bar-visibility', visibility);
window.restart = () => {
window.log.info('restart');
@ -213,10 +207,7 @@ window.onUnblockNumber = async number => {
const conversation = window.getConversationController().get(number);
await conversation.unblock();
} catch (e) {
window.log.info(
'IPC on unblock: failed to fetch conversation for number: ',
number
);
window.log.info('IPC on unblock: failed to fetch conversation for number: ', number);
}
}
};
@ -228,8 +219,7 @@ ipc.on('mediaPermissionsChanged', () => {
window.closeAbout = () => ipc.send('close-about');
window.readyForUpdates = () => ipc.send('ready-for-updates');
window.updateTrayIcon = unreadCount =>
ipc.send('update-tray-icon', unreadCount);
window.updateTrayIcon = unreadCount => ipc.send('update-tray-icon', unreadCount);
ipc.on('set-up-with-import', () => {
Whisper.events.trigger('setupWithImport');
@ -288,13 +278,11 @@ window.setSettingValue = (settingID, value) => {
};
window.getMediaPermissions = () => ipc.sendSync('get-media-permissions');
window.setMediaPermissions = value =>
ipc.send('set-media-permissions', !!value);
window.setMediaPermissions = value => ipc.send('set-media-permissions', !!value);
// Auto update setting
window.getAutoUpdateEnabled = () => ipc.sendSync('get-auto-update-setting');
window.setAutoUpdateEnabled = value =>
ipc.send('set-auto-update-setting', !!value);
window.setAutoUpdateEnabled = value => ipc.send('set-auto-update-setting', !!value);
ipc.on('get-ready-for-shutdown', async () => {
const { shutdown } = window.Events || {};
@ -308,10 +296,7 @@ ipc.on('get-ready-for-shutdown', async () => {
await shutdown();
ipc.send('now-ready-for-shutdown');
} catch (error) {
ipc.send(
'now-ready-for-shutdown',
error && error.stack ? error.stack : error
);
ipc.send('now-ready-for-shutdown', error && error.stack ? error.stack : error);
}
});
@ -411,8 +396,7 @@ window.models = require('./ts/models');
window.Signal = window.Signal || {};
window.Signal.Data = require('./ts/data/data');
window.getMessageController = () =>
window.libsession.Messages.MessageController.getInstance();
window.getMessageController = () => window.libsession.Messages.MessageController.getInstance();
window.getConversationController = () =>
window.libsession.Conversations.ConversationController.getInstance();
@ -422,9 +406,7 @@ window.Signal.Backup = require('./js/modules/backup');
window.Signal.Logs = require('./js/modules/logs');
window.addEventListener('contextmenu', e => {
const editable = e.target.closest(
'textarea, input, [contenteditable="true"]'
);
const editable = e.target.closest('textarea, input, [contenteditable="true"]');
const link = e.target.closest('a');
const selection = Boolean(window.getSelection().toString());
if (!editable && !selection && !link) {
@ -438,9 +420,7 @@ window.NewSnodeAPI = require('./ts/session/snode_api/serviceNodeAPI');
window.SnodePool = require('./ts/session/snode_api/snodePool');
if (process.env.USE_STUBBED_NETWORK) {
const {
SwarmPollingStub,
} = require('./ts/session/snode_api/swarmPollingStub');
const { SwarmPollingStub } = require('./ts/session/snode_api/swarmPollingStub');
window.SwarmPolling = new SwarmPollingStub();
} else {
const { SwarmPolling } = require('./ts/session/snode_api/swarmPolling');
@ -486,8 +466,6 @@ if (config.environment.includes('test-integration')) {
// Blocking
const {
BlockedNumberController,
} = require('./ts/util/blockedNumberController');
const { BlockedNumberController } = require('./ts/util/blockedNumberController');
window.BlockedNumberController = BlockedNumberController;

@ -356,8 +356,8 @@ $session-compose-margin: 20px;
.session-brand-logo {
height: 180px;
filter: brightness(0) saturate(100%) invert(75%) sepia(84%) saturate(3272%)
hue-rotate(103deg) brightness(106%) contrast(103%);
filter: brightness(0) saturate(100%) invert(75%) sepia(84%) saturate(3272%) hue-rotate(103deg)
brightness(106%) contrast(103%);
}
.session-text-logo {

@ -4,11 +4,7 @@ $color-loki-dark-gray: #323232;
$color-loki-extra-dark-gray: #101010;
$color-loki-green: #3bd110;
$color-loki-green-dark: #32b10e;
$color-loki-green-gradient: linear-gradient(
to right,
rgb(120, 190, 32) 0%,
rgb(0, 133, 34) 100%
);
$color-loki-green-gradient: linear-gradient(to right, rgb(120, 190, 32) 0%, rgb(0, 133, 34) 100%);
$color-white: #ffffff;
$color-gray-02: #f8f9f9;

@ -174,9 +174,7 @@ describe('app/logging', () => {
JSON.stringify({ time: '2018-01-04T19:17:02.014Z' }),
JSON.stringify({ time: '2018-01-04T19:17:03.014Z' }),
].join('\n');
const expected = [
JSON.stringify({ time: '2018-01-04T19:17:03.014Z' }),
].join('\n');
const expected = [JSON.stringify({ time: '2018-01-04T19:17:03.014Z' })].join('\n');
const target = path.join(basePath, 'log.log');
const files = [
@ -267,10 +265,7 @@ describe('app/logging', () => {
});
});
it('returns sorted entries from all files', () => {
const first = [
JSON.stringify({ msg: 2, time: '2018-01-04T19:17:05.014Z' }),
'',
].join('\n');
const first = [JSON.stringify({ msg: 2, time: '2018-01-04T19:17:05.014Z' }), ''].join('\n');
const second = [
JSON.stringify({ msg: 1, time: '2018-01-04T19:17:00.014Z' }),
JSON.stringify({ msg: 3, time: '2018-01-04T19:18:00.014Z' }),

@ -61,9 +61,7 @@ describe('SignalMenu', () => {
const { messages } = loadLocale({ appLocale, logger });
const actual = SignalMenu.createTemplate(options, messages);
const fixturePath = includeSetup
? fixtures.setup
: fixtures.default;
const fixturePath = includeSetup ? fixtures.setup : fixtures.default;
// eslint-disable-next-line global-require, import/no-dynamic-require
const fixture = require(fixturePath);
assert.deepEqual(actual, fixture);

@ -5,10 +5,8 @@ const { _urlToPath } = require('../../app/protocol_filter');
describe('Protocol Filter', () => {
describe('_urlToPath', () => {
it('returns proper file path for unix style file URI with hash', () => {
const path =
'file:///Users/someone/Development/signal/electron/background.html#first-page';
const expected =
'/Users/someone/Development/signal/electron/background.html';
const path = 'file:///Users/someone/Development/signal/electron/background.html#first-page';
const expected = '/Users/someone/Development/signal/electron/background.html';
const actual = _urlToPath(path);
expect(actual).to.equal(expected);
@ -17,8 +15,7 @@ describe('Protocol Filter', () => {
it('returns proper file path for unix style file URI with querystring', () => {
const path =
'file:///Users/someone/Development/signal/electron/background.html?name=Signal&locale=en&version=2.4.0';
const expected =
'/Users/someone/Development/signal/electron/background.html';
const expected = '/Users/someone/Development/signal/electron/background.html';
const actual = _urlToPath(path);
expect(actual).to.equal(expected);
@ -27,8 +24,7 @@ describe('Protocol Filter', () => {
it('returns proper file path for unix style file URI with hash and querystring', () => {
const path =
'file:///Users/someone/Development/signal/electron/background.html#somewhere?name=Signal';
const expected =
'/Users/someone/Development/signal/electron/background.html';
const expected = '/Users/someone/Development/signal/electron/background.html';
const actual = _urlToPath(path);
expect(actual).to.equal(expected);
@ -45,20 +41,16 @@ describe('Protocol Filter', () => {
});
it('translates from URL format to filesystem format', () => {
const path =
'file:///Users/someone/Development%20Files/signal/electron/background.html';
const expected =
'/Users/someone/Development Files/signal/electron/background.html';
const path = 'file:///Users/someone/Development%20Files/signal/electron/background.html';
const expected = '/Users/someone/Development Files/signal/electron/background.html';
const actual = _urlToPath(path);
expect(actual).to.equal(expected);
});
it('translates from URL format to filesystem format', () => {
const path =
'file:///Users/someone/Development%20Files/signal/electron/background.html';
const expected =
'/Users/someone/Development Files/signal/electron/background.html';
const path = 'file:///Users/someone/Development%20Files/signal/electron/background.html';
const expected = '/Users/someone/Development Files/signal/electron/background.html';
const actual = _urlToPath(path);
expect(actual).to.equal(expected);

@ -27,8 +27,7 @@ describe('Backup', () => {
});
it('handles a file with a long extension', () => {
const initial =
'0123456789012345678901234567890123456789.01234567890123456789';
const initial = '0123456789012345678901234567890123456789.01234567890123456789';
const expected = '012345678901234567890123456789';
assert.strictEqual(Signal.Backup._trimFileName(initial), expected);
});
@ -51,11 +50,7 @@ describe('Backup', () => {
};
const expected = 'blah.jpg';
const actual = Signal.Backup._getExportAttachmentFileName(
message,
index,
attachment
);
const actual = Signal.Backup._getExportAttachmentFileName(message, index, attachment);
assert.strictEqual(actual, expected);
});
@ -69,11 +64,7 @@ describe('Backup', () => {
};
const expected = '123';
const actual = Signal.Backup._getExportAttachmentFileName(
message,
index,
attachment
);
const actual = Signal.Backup._getExportAttachmentFileName(message, index, attachment);
assert.strictEqual(actual, expected);
});
@ -88,11 +79,7 @@ describe('Backup', () => {
};
const expected = '123.jpeg';
const actual = Signal.Backup._getExportAttachmentFileName(
message,
index,
attachment
);
const actual = Signal.Backup._getExportAttachmentFileName(message, index, attachment);
assert.strictEqual(actual, expected);
});
@ -107,11 +94,7 @@ describe('Backup', () => {
};
const expected = '123.something';
const actual = Signal.Backup._getExportAttachmentFileName(
message,
index,
attachment
);
const actual = Signal.Backup._getExportAttachmentFileName(message, index, attachment);
assert.strictEqual(actual, expected);
});
});
@ -128,11 +111,7 @@ describe('Backup', () => {
};
const expected = 'id-45';
const actual = Signal.Backup._getAnonymousAttachmentFileName(
message,
index,
attachment
);
const actual = Signal.Backup._getAnonymousAttachmentFileName(message, index, attachment);
assert.strictEqual(actual, expected);
});
@ -147,11 +126,7 @@ describe('Backup', () => {
};
const expected = 'id-45-1';
const actual = Signal.Backup._getAnonymousAttachmentFileName(
message,
index,
attachment
);
const actual = Signal.Backup._getAnonymousAttachmentFileName(message, index, attachment);
assert.strictEqual(actual, expected);
});
});
@ -164,10 +139,7 @@ describe('Backup', () => {
id: 'id',
};
const expected = '123 (012345678901234567890123456789 id)';
assert.strictEqual(
Signal.Backup._getConversationDirName(conversation),
expected
);
assert.strictEqual(Signal.Backup._getConversationDirName(conversation), expected);
});
it('uses just id if name is not available', () => {
@ -176,10 +148,7 @@ describe('Backup', () => {
id: 'id',
};
const expected = '123 (id)';
assert.strictEqual(
Signal.Backup._getConversationDirName(conversation),
expected
);
assert.strictEqual(Signal.Backup._getConversationDirName(conversation), expected);
});
it('uses inactive for missing active_at', () => {
@ -188,10 +157,7 @@ describe('Backup', () => {
id: 'id',
};
const expected = 'inactive (name id)';
assert.strictEqual(
Signal.Backup._getConversationDirName(conversation),
expected
);
assert.strictEqual(Signal.Backup._getConversationDirName(conversation), expected);
});
});
@ -203,10 +169,7 @@ describe('Backup', () => {
type: 'private',
};
const expected = '123 (id)';
assert.strictEqual(
Signal.Backup._getConversationLoggingName(conversation),
expected
);
assert.strictEqual(Signal.Backup._getConversationLoggingName(conversation), expected);
});
it('uses just id if name is not available', () => {
@ -216,10 +179,7 @@ describe('Backup', () => {
type: 'group',
};
const expected = '123 ([REDACTED_GROUP]pId)';
assert.strictEqual(
Signal.Backup._getConversationLoggingName(conversation),
expected
);
assert.strictEqual(Signal.Backup._getConversationLoggingName(conversation), expected);
});
it('uses inactive for missing active_at', () => {
@ -228,10 +188,7 @@ describe('Backup', () => {
type: 'private',
};
const expected = 'inactive (id)';
assert.strictEqual(
Signal.Backup._getConversationLoggingName(conversation),
expected
);
assert.strictEqual(Signal.Backup._getConversationLoggingName(conversation), expected);
});
});
@ -245,17 +202,12 @@ describe('Backup', () => {
// because it always fails due to lstat permission error.
// Don't know how to fix it so this is a temp work around.
if (isWindows || !isWindows) {
console.log(
'Skipping exports then imports to produce the same data we started'
);
console.log('Skipping exports then imports to produce the same data we started');
this.skip();
return;
}
const {
upgradeMessageSchema,
loadAttachmentData,
} = window.Signal.Migrations;
const { upgradeMessageSchema, loadAttachmentData } = window.Signal.Migrations;
const staticKeyPair = await libsignal.KeyHelper.generateIdentityKeyPair();
const attachmentsPattern = path.join(attachmentsPath, '**');
@ -278,9 +230,7 @@ describe('Backup', () => {
jpg: getFixture('fixtures/koushik-chowdavarapu-105425-unsplash.jpg'),
mp3: getFixture('fixtures/incompetech-com-Agnus-Dei-X.mp3'),
txt: getFixture('fixtures/lorem-ipsum.txt'),
png: getFixture(
'fixtures/freepngs-2cd43b_bed7d1327e88454487397574d87b64dc_mv2.png'
),
png: getFixture('fixtures/freepngs-2cd43b_bed7d1327e88454487397574d87b64dc_mv2.png'),
};
async function wrappedLoadAttachment(attachment) {
@ -308,8 +258,7 @@ describe('Backup', () => {
Object.entries(object)
.filter(([, value]) => value === undefined)
.map(([name]) => name);
const omitUndefinedKeys = object =>
_.omit(object, getUndefinedKeys(object));
const omitUndefinedKeys = object => _.omit(object, getUndefinedKeys(object));
// We want to know which paths have two slashes, since that tells us which files
// in the attachment fan-out are files vs. directories.
@ -340,14 +289,11 @@ describe('Backup', () => {
});
};
const quotedAttachments =
(message.quote && message.quote.attachments) || [];
const quotedAttachments = (message.quote && message.quote.attachments) || [];
return Object.assign({}, message, {
quote: Object.assign({}, message.quote, {
attachments: await Promise.all(
quotedAttachments.map(wrappedMapper)
),
attachments: await Promise.all(quotedAttachments.map(wrappedMapper)),
}),
});
};
@ -369,9 +315,7 @@ describe('Backup', () => {
return contact && contact.avatar && contact.avatar.avatar
? Object.assign({}, contact, {
avatar: Object.assign({}, contact.avatar, {
avatar: await wrappedLoadAttachment(
contact.avatar.avatar
),
avatar: await wrappedLoadAttachment(contact.avatar.avatar),
}),
})
: contact;
@ -518,9 +462,7 @@ describe('Backup', () => {
console.log({ conversation });
await window.Signal.Data.saveConversation(conversation);
console.log(
'Backup test: Ensure that all attachments were saved to disk'
);
console.log('Backup test: Ensure that all attachments were saved to disk');
const attachmentFiles = removeDirs(glob.sync(attachmentsPattern));
console.log({ attachmentFiles });
assert.strictEqual(ATTACHMENT_COUNT, attachmentFiles.length);
@ -537,9 +479,7 @@ describe('Backup', () => {
const messageZipExists = fse.existsSync(archivePath);
assert.strictEqual(true, messageZipExists);
console.log(
'Backup test: Ensure that all attachments made it to backup dir'
);
console.log('Backup test: Ensure that all attachments made it to backup dir');
const backupAttachmentPattern = path.join(backupDir, 'attachments/*');
const backupAttachments = glob.sync(backupAttachmentPattern);
console.log({ backupAttachments });
@ -569,10 +509,7 @@ describe('Backup', () => {
];
const conversationFromDB = conversationCollection.at(0).attributes;
console.log({ conversationFromDB, conversation });
assert.deepEqual(
_.omit(conversationFromDB, ommited),
_.omit(conversation, ommited)
);
assert.deepEqual(_.omit(conversationFromDB, ommited), _.omit(conversation, ommited));
console.log('Backup test: Check messages');
const allMessages = await window.Signal.Data.getAllMessages();
@ -583,19 +520,13 @@ describe('Backup', () => {
assert.deepEqual(messageFromDB, expectedMessage);
console.log('Backup test: ensure that all attachments were imported');
const recreatedAttachmentFiles = removeDirs(
glob.sync(attachmentsPattern)
);
const recreatedAttachmentFiles = removeDirs(glob.sync(attachmentsPattern));
console.log({ recreatedAttachmentFiles });
assert.strictEqual(ATTACHMENT_COUNT, recreatedAttachmentFiles.length);
assert.deepEqual(attachmentFiles, recreatedAttachmentFiles);
console.log(
'Backup test: Check that all attachments were successfully imported'
);
const messageWithAttachmentsFromDB = await loadAllFilesFromDisk(
messageFromDB
);
console.log('Backup test: Check that all attachments were successfully imported');
const messageWithAttachmentsFromDB = await loadAllFilesFromDisk(messageFromDB);
const expectedMessageWithAttachments = await loadAllFilesFromDisk(
omitUndefinedKeys(message)
);

@ -19,10 +19,7 @@ describe('Crypto', () => {
describe('symmetric encryption', () => {
it('roundtrips', async () => {
const message = 'this is my message';
const plaintext = dcodeIO.ByteBuffer.wrap(
message,
'binary'
).toArrayBuffer();
const plaintext = dcodeIO.ByteBuffer.wrap(message, 'binary').toArrayBuffer();
const key = textsecure.crypto.getRandomBytes(32);
const encrypted = await Signal.Crypto.encryptSymmetric(key, plaintext);
@ -36,10 +33,7 @@ describe('Crypto', () => {
it('roundtrip fails if nonce is modified', async () => {
const message = 'this is my message';
const plaintext = dcodeIO.ByteBuffer.wrap(
message,
'binary'
).toArrayBuffer();
const plaintext = dcodeIO.ByteBuffer.wrap(message, 'binary').toArrayBuffer();
const key = textsecure.crypto.getRandomBytes(32);
const encrypted = await Signal.Crypto.encryptSymmetric(key, plaintext);
@ -61,10 +55,7 @@ describe('Crypto', () => {
it('roundtrip fails if mac is modified', async () => {
const message = 'this is my message';
const plaintext = dcodeIO.ByteBuffer.wrap(
message,
'binary'
).toArrayBuffer();
const plaintext = dcodeIO.ByteBuffer.wrap(message, 'binary').toArrayBuffer();
const key = textsecure.crypto.getRandomBytes(32);
const encrypted = await Signal.Crypto.encryptSymmetric(key, plaintext);
@ -86,10 +77,7 @@ describe('Crypto', () => {
it('roundtrip fails if encrypted contents are modified', async () => {
const message = 'this is my message';
const plaintext = dcodeIO.ByteBuffer.wrap(
message,
'binary'
).toArrayBuffer();
const plaintext = dcodeIO.ByteBuffer.wrap(message, 'binary').toArrayBuffer();
const key = textsecure.crypto.getRandomBytes(32);
const encrypted = await Signal.Crypto.encryptSymmetric(key, plaintext);
@ -115,8 +103,7 @@ describe('Crypto', () => {
const staticKeyPair = await libsignal.KeyHelper.generateIdentityKeyPair();
const message = 'this is my message';
const plaintext = Signal.Crypto.bytesFromString(message);
const path =
'fa/facdf99c22945b1c9393345599a276f4b36ad7ccdc8c2467f5441b742c2d11fa';
const path = 'fa/facdf99c22945b1c9393345599a276f4b36ad7ccdc8c2467f5441b742c2d11fa';
const encrypted = await Signal.Crypto.encryptAttachment(
staticKeyPair.pubKey.slice(1),

@ -15,10 +15,7 @@ describe('Fixtures', () => {
await window
.getConversationController()
.getOrCreateAndWait(
window.libsession.Utils.UserUtils.getOurPubKeyStrFromCache(),
'private'
);
.getOrCreateAndWait(window.libsession.Utils.UserUtils.getOurPubKeyStrFromCache(), 'private');
});
it('renders', async () => {

@ -14,10 +14,7 @@ describe('i18n', () => {
});
it('returns message with multiple substitutions', () => {
const actual = i18n('theyChangedTheTimer', ['Someone', '5 minutes']);
assert.equal(
actual,
'Someone set the disappearing message timer to 5 minutes'
);
assert.equal(actual, 'Someone set the disappearing message timer to 5 minutes');
});
});

@ -15,8 +15,7 @@ describe('Privacy', () => {
const actual = Privacy.redactSessionID(text);
const expected =
'This is a log line with a session ID [REDACTED]\n' +
'and another one [REDACTED]';
'This is a log line with a session ID [REDACTED]\n' + 'and another one [REDACTED]';
assert.equal(actual, expected);
});
@ -33,8 +32,7 @@ describe('Privacy', () => {
describe('redactGroupIds', () => {
it('should redact all group IDs', () => {
const text =
'This is a log line with two group IDs: group(123456789)\n' +
'and group(abcdefghij)';
'This is a log line with two group IDs: group(123456789)\n' + 'and group(abcdefghij)';
const actual = Privacy.redactGroupIds(text);
const expected =
@ -45,8 +43,7 @@ describe('Privacy', () => {
it('should remove newlines from redacted group IDs', () => {
const text =
'This is a log line with two group IDs: group(12345678\n9)\n' +
'and group(abc\ndefghij)';
'This is a log line with two group IDs: group(12345678\n9)\n' + 'and group(abc\ndefghij)';
const actual = Privacy.redactGroupIds(text);
const expected =
@ -126,10 +123,8 @@ describe('Privacy', () => {
});
it('should redact stack traces with both forward and backslashes', () => {
const testPath =
'C:/Users/Meow/AppData/Local/Programs/loki-messenger-beta';
const modifiedTestPath =
'C:\\Users\\Meow\\AppData\\Local\\Programs\\loki-messenger-beta';
const testPath = 'C:/Users/Meow/AppData/Local/Programs/loki-messenger-beta';
const modifiedTestPath = 'C:\\Users\\Meow\\AppData\\Local\\Programs\\loki-messenger-beta';
const text =
'This is a log line with sensitive information:\n' +
`path1 ${testPath}\\main.js\n` +
@ -148,8 +143,7 @@ describe('Privacy', () => {
});
it('should redact stack traces with escaped backslashes', () => {
const testPath =
'C:\\Users\\Meow\\AppData\\Local\\Programs\\loki-messenger-beta';
const testPath = 'C:\\Users\\Meow\\AppData\\Local\\Programs\\loki-messenger-beta';
const modifiedTestPath =
'C:\\\\Users\\\\Meow\\\\AppData\\\\Local\\\\Programs\\\\loki-messenger-beta';
const text =

@ -3,9 +3,7 @@ require('mocha-testcheck').install();
const { assert } = require('chai');
const Attachment = require('../../../js/modules/types/attachment');
const {
stringToArrayBuffer,
} = require('../../../js/modules/string_to_array_buffer');
const { stringToArrayBuffer } = require('../../../js/modules/string_to_array_buffer');
describe('Attachment', () => {
describe('replaceUnicodeOrderOverrides', () => {

@ -14,11 +14,7 @@ describe('Errors', () => {
const formattedError = Errors.toLogFormat(error);
assert.include(formattedError, 'errors_test.js');
assert.include(
formattedError,
APP_ROOT_PATH,
'Formatted stack has app path'
);
assert.include(formattedError, APP_ROOT_PATH, 'Formatted stack has app path');
});
it('should return error string representation if stack is missing', () => {

@ -3,9 +3,7 @@ const sinon = require('sinon');
const Message = require('../../../js/modules/types/message');
const { SignalService } = require('../../../ts/protobuf');
const {
stringToArrayBuffer,
} = require('../../../js/modules/string_to_array_buffer');
const { stringToArrayBuffer } = require('../../../js/modules/string_to_array_buffer');
describe('Message', () => {
const logger = {
@ -77,10 +75,7 @@ describe('Message', () => {
const writeExistingAttachmentData = attachment => {
assert.equal(attachment.path, 'ab/abcdefghi');
assert.deepEqual(
attachment.data,
stringToArrayBuffer('Its easy if you try')
);
assert.deepEqual(attachment.data, stringToArrayBuffer('Its easy if you try'));
};
const actual = await Message.createAttachmentDataWriter({
@ -125,10 +120,7 @@ describe('Message', () => {
const writeExistingAttachmentData = attachment => {
assert.equal(attachment.path, 'ab/abcdefghi');
assert.deepEqual(
attachment.data,
stringToArrayBuffer('Its easy if you try')
);
assert.deepEqual(attachment.data, stringToArrayBuffer('Its easy if you try'));
};
const actual = await Message.createAttachmentDataWriter({
@ -176,10 +168,7 @@ describe('Message', () => {
const writeExistingAttachmentData = attachment => {
assert.equal(attachment.path, 'ab/abcdefghi');
assert.deepEqual(
attachment.data,
stringToArrayBuffer('Its easy if you try')
);
assert.deepEqual(attachment.data, stringToArrayBuffer('Its easy if you try'));
};
const actual = await Message.createAttachmentDataWriter({
@ -291,9 +280,7 @@ describe('Message', () => {
contact: [],
};
const expectedAttachmentData = stringToArrayBuffer(
'Its easy if you try'
);
const expectedAttachmentData = stringToArrayBuffer('Its easy if you try');
const context = {
writeNewAttachmentData: async attachmentData => {
assert.deepEqual(attachmentData, expectedAttachmentData);
@ -340,13 +327,11 @@ describe('Message', () => {
schemaVersion: 1,
};
const v1 = async message =>
Object.assign({}, message, { hasUpgradedToVersion1: true });
const v1 = async message => Object.assign({}, message, { hasUpgradedToVersion1: true });
const v2 = async () => {
throw new Error('boom');
};
const v3 = async message =>
Object.assign({}, message, { hasUpgradedToVersion3: true });
const v3 = async message => Object.assign({}, message, { hasUpgradedToVersion3: true });
const toVersion1 = Message._withSchemaVersion({
schemaVersion: 1,
@ -363,10 +348,7 @@ describe('Message', () => {
const context = { logger };
const upgradeSchema = async message =>
toVersion3(
await toVersion2(await toVersion1(message, context), context),
context
);
toVersion3(await toVersion2(await toVersion1(message, context), context), context);
const actual = await upgradeSchema(input);
assert.deepEqual(actual, expected);
@ -421,10 +403,7 @@ describe('Message', () => {
const context = { logger };
// NOTE: We upgrade to 3 before 2, i.e. the pipeline should abort:
const upgradeSchema = async attachment =>
toVersion2(
await toVersion3(await toVersion1(attachment, context), context),
context
);
toVersion2(await toVersion3(await toVersion1(attachment, context), context), context);
const actual = await upgradeSchema(input);
assert.deepEqual(actual, expected);
@ -436,8 +415,7 @@ describe('Message', () => {
it('should require a version number', () => {
const toVersionX = () => {};
assert.throws(
() =>
Message._withSchemaVersion({ schemaVersion: toVersionX, upgrade: 2 }),
() => Message._withSchemaVersion({ schemaVersion: toVersionX, upgrade: 2 }),
'_withSchemaVersion: schemaVersion is invalid'
);
});
@ -450,8 +428,7 @@ describe('Message', () => {
});
it('should skip upgrading if message has already been upgraded', async () => {
const upgrade = async message =>
Object.assign({}, message, { foo: true });
const upgrade = async message => Object.assign({}, message, { foo: true });
const upgradeWithVersion = Message._withSchemaVersion({
schemaVersion: 3,
upgrade,
@ -512,9 +489,7 @@ describe('Message', () => {
describe('_mapQuotedAttachments', () => {
it('handles message with no quote', async () => {
const upgradeAttachment = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeAttachment = sinon.stub().throws(new Error("Shouldn't be called"));
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
const message = {
@ -525,9 +500,7 @@ describe('Message', () => {
});
it('handles quote with no attachments', async () => {
const upgradeAttachment = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeAttachment = sinon.stub().throws(new Error("Shouldn't be called"));
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
const message = {
@ -548,9 +521,7 @@ describe('Message', () => {
});
it('handles zero attachments', async () => {
const upgradeAttachment = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeAttachment = sinon.stub().throws(new Error("Shouldn't be called"));
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
const message = {
@ -565,9 +536,7 @@ describe('Message', () => {
});
it('handles attachments with no thumbnail', async () => {
const upgradeAttachment = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeAttachment = sinon.stub().throws(new Error("Shouldn't be called"));
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
const message = {
@ -587,9 +556,7 @@ describe('Message', () => {
});
it('does not eliminate thumbnails with missing data field', async () => {
const upgradeAttachment = sinon
.stub()
.returns({ fileName: 'processed!' });
const upgradeAttachment = sinon.stub().returns({ fileName: 'processed!' });
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
const message = {
@ -665,9 +632,7 @@ describe('Message', () => {
describe('_mapContact', () => {
it('handles message with no contact field', async () => {
const upgradeContact = sinon
.stub()
.throws(new Error("Shouldn't be called"));
const upgradeContact = sinon.stub().throws(new Error("Shouldn't be called"));
const upgradeVersion = Message._mapContact(upgradeContact);
const message = {

@ -6,10 +6,7 @@ import { default as glob } from 'glob';
import fse from 'fs-extra';
import toArrayBuffer from 'to-arraybuffer';
import { isArrayBuffer, isString, map } from 'lodash';
import {
decryptAttachmentBuffer,
encryptAttachmentBuffer,
} from '../../ts/types/Attachment';
import { decryptAttachmentBuffer, encryptAttachmentBuffer } from '../../ts/types/Attachment';
const PATH = 'attachments.noindex';
@ -111,9 +108,7 @@ export const createWriterForExisting = (root: any) => {
}
await fse.ensureFile(normalized);
const { encryptedBufferWithHeader } = await encryptAttachmentBuffer(
arrayBuffer
);
const { encryptedBufferWithHeader } = await encryptAttachmentBuffer(arrayBuffer);
const buffer = Buffer.from(encryptedBufferWithHeader.buffer);
await fse.writeFile(normalized, buffer);
@ -175,9 +170,7 @@ export const getRelativePath = (name: any) => {
};
// createAbsolutePathGetter :: RootPath -> RelativePath -> AbsolutePath
export const createAbsolutePathGetter = (rootPath: string) => (
relativePath: string
) => {
export const createAbsolutePathGetter = (rootPath: string) => (relativePath: string) => {
const absolutePath = path.join(rootPath, relativePath);
const normalized = path.normalize(absolutePath);
if (!normalized.startsWith(rootPath)) {

@ -47,13 +47,7 @@ const NoImage = (props: {
const { memberAvatars, size } = props;
// if no image but we have conversations set for the group, renders group members avatars
if (memberAvatars) {
return (
<ClosedGroupAvatar
size={size}
memberAvatars={memberAvatars}
i18n={window.i18n}
/>
);
return <ClosedGroupAvatar size={size} memberAvatars={memberAvatars} i18n={window.i18n} />;
}
return <Identicon {...props} />;
@ -87,10 +81,7 @@ export const Avatar = (props: Props) => {
const { urlToLoad } = useEncryptedFileFetch(avatarPath || '', '');
const handleImageError = () => {
window.log.warn(
'Avatar: Image failed to load; failing over to placeholder',
urlToLoad
);
window.log.warn('Avatar: Image failed to load; failing over to placeholder', urlToLoad);
setImageBroken(true);
};

@ -11,10 +11,7 @@ type Props = {
const sha512FromPubkey = async (pubkey: string): Promise<string> => {
// tslint:disable-next-line: await-promise
const buf = await crypto.subtle.digest(
'SHA-512',
new TextEncoder().encode(pubkey)
);
const buf = await crypto.subtle.digest('SHA-512', new TextEncoder().encode(pubkey));
// tslint:disable: prefer-template restrict-plus-operands
return Array.prototype.map

@ -24,9 +24,7 @@ export class ClosedGroupAvatar extends React.PureComponent<Props> {
case AvatarSize.HUGE:
return AvatarSize.XL;
default:
throw new Error(
`Invalid size request for closed group avatar: ${size}`
);
throw new Error(`Invalid size request for closed group avatar: ${size}`);
}
}

@ -6,11 +6,7 @@ import * as GoogleChrome from '../util/GoogleChrome';
import { AttachmentType } from '../types/Attachment';
import { SessionInput } from './session/SessionInput';
import {
SessionButton,
SessionButtonColor,
SessionButtonType,
} from './session/SessionButton';
import { SessionButton, SessionButtonColor, SessionButtonType } from './session/SessionButton';
import { darkTheme, lightTheme } from '../state/ducks/SessionTheme';
interface Props {
@ -87,14 +83,8 @@ export class CaptionEditor extends React.Component<Props, State> {
return (
<div role="dialog" className="module-caption-editor">
<div
role="button"
onClick={onClose}
className="module-caption-editor__close-button"
/>
<div className="module-caption-editor__media-container">
{this.renderObject()}
</div>
<div role="button" onClick={onClose} className="module-caption-editor__close-button" />
<div className="module-caption-editor__media-container">{this.renderObject()}</div>
<div className="module-caption-editor__bottom-bar">
<div className="module-caption-editor__input-container">
<SessionInput

@ -23,12 +23,7 @@ export class ContactListItem extends React.Component<Props> {
const userName = name || profileName || phoneNumber;
return (
<Avatar
avatarPath={avatarPath}
name={userName}
size={AvatarSize.S}
pubkey={phoneNumber}
/>
<Avatar avatarPath={avatarPath} name={userName} size={AvatarSize.S} pubkey={phoneNumber} />
);
}
@ -42,11 +37,7 @@ export class ContactListItem extends React.Component<Props> {
!isMe && profileName && !name ? (
<span className="module-contact-list-item__text__profile-name">
~
<Emojify
text={profileName}
i18n={i18n}
key={`emojify-list-item-${phoneNumber}`}
/>
<Emojify text={profileName} i18n={i18n} key={`emojify-list-item-${phoneNumber}`} />
</span>
) : null;

@ -49,10 +49,7 @@ type PropsHousekeeping = {
type Props = ConversationListItemProps & PropsHousekeeping;
const Portal = ({ children }: { children: any }) => {
return createPortal(
children,
document.querySelector('.inbox.index') as Element
);
return createPortal(children, document.querySelector('.inbox.index') as Element);
};
class ConversationListItem extends React.PureComponent<Props> {
@ -61,13 +58,7 @@ class ConversationListItem extends React.PureComponent<Props> {
}
public renderAvatar() {
const {
avatarPath,
name,
phoneNumber,
profileName,
memberAvatars,
} = this.props;
const { avatarPath, name, phoneNumber, profileName, memberAvatars } = this.props;
const userName = name || profileName || phoneNumber;
@ -91,11 +82,7 @@ class ConversationListItem extends React.PureComponent<Props> {
let unreadCountDiv = null;
if (unreadCount > 0) {
atSymbol = mentionedUs ? <p className="at-symbol">@</p> : null;
unreadCountDiv = (
<p className="module-conversation-list-item__unread-count">
{unreadCount}
</p>
);
unreadCountDiv = <p className="module-conversation-list-item__unread-count">{unreadCount}</p>;
}
return (
@ -103,9 +90,7 @@ class ConversationListItem extends React.PureComponent<Props> {
<div
className={classNames(
'module-conversation-list-item__header__name',
unreadCount > 0
? 'module-conversation-list-item__header__name--with-unread'
: null
unreadCount > 0 ? 'module-conversation-list-item__header__name--with-unread' : null
)}
>
{this.renderUser()}
@ -116,9 +101,7 @@ class ConversationListItem extends React.PureComponent<Props> {
<div
className={classNames(
'module-conversation-list-item__header__date',
unreadCount > 0
? 'module-conversation-list-item__header__date--has-unread'
: null
unreadCount > 0 ? 'module-conversation-list-item__header__date--has-unread' : null
)}
>
{
@ -152,9 +135,7 @@ class ConversationListItem extends React.PureComponent<Props> {
<div
className={classNames(
'module-conversation-list-item__message__text',
unreadCount > 0
? 'module-conversation-list-item__message__text--has-unread'
: null
unreadCount > 0 ? 'module-conversation-list-item__message__text--has-unread' : null
)}
>
{isTyping ? (
@ -212,12 +193,8 @@ class ConversationListItem extends React.PureComponent<Props> {
style={style}
className={classNames(
'module-conversation-list-item',
unreadCount > 0
? 'module-conversation-list-item--has-unread'
: null,
unreadCount > 0 && mentionedUs
? 'module-conversation-list-item--mentioned-us'
: null,
unreadCount > 0 ? 'module-conversation-list-item--has-unread' : null,
unreadCount > 0 && mentionedUs ? 'module-conversation-list-item--mentioned-us' : null,
isSelected ? 'module-conversation-list-item--is-selected' : null,
isBlocked ? 'module-conversation-list-item--is-blocked' : null
)}

@ -4,17 +4,9 @@ import { QRCode } from 'react-qr-svg';
import { Avatar, AvatarSize } from './Avatar';
import {
SessionButton,
SessionButtonColor,
SessionButtonType,
} from './session/SessionButton';
import {
SessionIconButton,
SessionIconSize,
SessionIconType,
} from './session/icon';
import { SessionButton, SessionButtonColor, SessionButtonType } from './session/SessionButton';
import { SessionIconButton, SessionIconSize, SessionIconType } from './session/icon';
import { SessionModal } from './session/SessionModal';
import { PillDivider } from './session/PillDivider';
import { ToastUtils, UserUtils } from '../session/utils';
@ -104,14 +96,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
<div className="session-id-section">
<PillDivider text={window.i18n('yourSessionID')} />
<p
className={classNames(
'text-selectable',
'session-id-section-display'
)}
>
{sessionID}
</p>
<p className={classNames('text-selectable', 'session-id-section-display')}>{sessionID}</p>
<div className="spacer-lg" />
<SessionSpinner loading={this.state.loading} />
@ -149,11 +134,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
<div className="avatar-center">
<div className="avatar-center-inner">
{this.renderAvatar()}
<div
className="image-upload-section"
role="button"
onClick={this.fireInputEvent}
/>
<div className="image-upload-section" role="button" onClick={this.fireInputEvent} />
<input
type="file"
ref={this.inputEl}
@ -237,12 +218,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
return (
<div className="qr-image">
<QRCode
value={sessionID}
bgColor={bgColor}
fgColor={fgColor}
level="L"
/>
<QRCode value={sessionID} bgColor={bgColor} fgColor={fgColor} level="L" />
</div>
);
}
@ -261,14 +237,7 @@ export class EditProfileDialog extends React.Component<Props, State> {
const { pubkey } = this.props;
const userName = profileName || pubkey;
return (
<Avatar
avatarPath={avatar}
name={userName}
size={AvatarSize.XL}
pubkey={pubkey}
/>
);
return <Avatar avatarPath={avatar} name={userName} size={AvatarSize.XL} pubkey={pubkey} />;
}
private onNameEdited(event: any) {

@ -22,9 +22,7 @@ export class Intl extends React.Component<Props> {
if (!components || !components.length || components.length <= index) {
// tslint:disable-next-line no-console
console.log(
`Error: Intl missing provided components for id ${id}, index ${index}`
);
console.log(`Error: Intl missing provided components for id ${id}, index ${index}`);
return;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save