more cleanup

This commit is contained in:
Audric Ackermann 2021-10-04 10:56:54 +11:00
parent d02d77a212
commit 1e8c8991ad
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4
54 changed files with 102 additions and 12692 deletions

View File

@ -1,12 +0,0 @@
{
"S3PublishEndpoints": {
"signal-desktop-apt": {
"region": "us-east-1",
"bucket": "updates.signal.org",
"prefix": "desktop/apt",
"acl": "public-read",
"plusWorkaround": false,
"disableMultiDel": false
}
}
}

View File

@ -32,8 +32,7 @@ js/util_worker.js
libtextsecure/test/blanket_mocha.js libtextsecure/test/blanket_mocha.js
mnemonic_languages/** mnemonic_languages/**
# Managed by package manager (`bower` and `yarn`/`npm`): # Managed by package manager (`yarn`/`npm`):
/bower.json
/package.json /package.json
# Symlink into third-party `components`: # Symlink into third-party `components`:

View File

@ -5,18 +5,32 @@ const sass = require('node-sass');
/* eslint-disable more/no-then, no-console */ /* eslint-disable more/no-then, no-console */
const toConcatForApp = [
'node_modules/jquery/dist/jquery.js',
'node_modules/bytebuffer/dist/bytebuffer.min.js',
'node_modules/long/dist/long.js',
'components/protobuf/**/*.js',
'node_modules/mustache/mustache.js',
'node_modules/underscore/underscore.js',
'node_modules/backbone/backbone.js',
];
const toConcatForComponentTextsecure = [
'node_modules/long/dist/long.js',
'components/protobuf/**/*.js',
];
module.exports = grunt => { module.exports = grunt => {
const bower = grunt.file.readJSON('bower.json');
const components = []; const components = [];
// eslint-disable-next-line guard-for-in, no-restricted-syntax // eslint-disable-next-line guard-for-in, no-restricted-syntax
for (const i in bower.concat.app) { for (const i in toConcatForApp) {
components.push(bower.concat.app[i]); components.push(toConcatForApp[i]);
} }
const libtextsecurecomponents = []; const libtextsecurecomponents = [];
// eslint-disable-next-line guard-for-in, no-restricted-syntax // eslint-disable-next-line guard-for-in, no-restricted-syntax
for (const i in bower.concat.libtextsecure) { for (const i in toConcatForComponentTextsecure) {
libtextsecurecomponents.push(bower.concat.libtextsecure[i]); libtextsecurecomponents.push(toConcatForComponentTextsecure[i]);
} }
const utilWorkerComponents = [ const utilWorkerComponents = [
@ -58,13 +72,7 @@ module.exports = grunt => {
'libtextsecure/crypto.js', 'libtextsecure/crypto.js',
'libtextsecure/storage.js', 'libtextsecure/storage.js',
'libtextsecure/storage/user.js', 'libtextsecure/storage/user.js',
'libtextsecure/storage/groups.js',
'libtextsecure/protobufs.js',
'libtextsecure/helpers.js', 'libtextsecure/helpers.js',
'libtextsecure/stringview.js',
'libtextsecure/event_target.js',
'libtextsecure/http-resources.js',
'libtextsecure/message_receiver.js',
], ],
dest: 'js/libtextsecure.js', dest: 'js/libtextsecure.js',
}, },

View File

@ -453,5 +453,6 @@
"unableToCall": "cancel your ongoing call first", "unableToCall": "cancel your ongoing call first",
"unableToCallTitle": "Cannot start new call", "unableToCallTitle": "Cannot start new call",
"callMissed": "Missed call from $name$", "callMissed": "Missed call from $name$",
"callMissedTitle": "Call missed" "callMissedTitle": "Call missed",
"startVideoCall": "Start Video Call"
} }

View File

@ -32,9 +32,7 @@
--> -->
<script type="text/javascript" src="js/components.js"></script> <script type="text/javascript" src="js/components.js"></script>
<script type="text/javascript" src="js/database.js"></script>
<script type="text/javascript" src="js/storage.js"></script> <script type="text/javascript" src="js/storage.js"></script>
<script type="text/javascript" src="js/legacy_storage.js"></script>
<script type="text/javascript" src="js/libtextsecure.js"></script> <script type="text/javascript" src="js/libtextsecure.js"></script>
<script type="text/javascript" src="js/focus_listener.js"></script> <script type="text/javascript" src="js/focus_listener.js"></script>
@ -43,7 +41,6 @@
<script type="text/javascript" src="js/read_syncs.js"></script> <script type="text/javascript" src="js/read_syncs.js"></script>
<script type="text/javascript" src="js/expiring_messages.js"></script> <script type="text/javascript" src="js/expiring_messages.js"></script>
<script type="text/javascript" src="js/chromium.js"></script>
<script type="text/javascript" src="js/registration.js"></script> <script type="text/javascript" src="js/registration.js"></script>
<script type="text/javascript" src="js/views/react_wrapper_view.js"></script> <script type="text/javascript" src="js/views/react_wrapper_view.js"></script>

View File

@ -32,9 +32,7 @@
--> -->
<script type="text/javascript" src="js/components.js"></script> <script type="text/javascript" src="js/components.js"></script>
<script type="text/javascript" src="js/database.js"></script>
<script type="text/javascript" src="js/storage.js"></script> <script type="text/javascript" src="js/storage.js"></script>
<script type="text/javascript" src="js/legacy_storage.js"></script>
<script type="text/javascript" src="js/libtextsecure.js"></script> <script type="text/javascript" src="js/libtextsecure.js"></script>
<script type="text/javascript" src="js/focus_listener.js"></script> <script type="text/javascript" src="js/focus_listener.js"></script>
@ -43,7 +41,6 @@
<script type="text/javascript" src="js/read_syncs.js"></script> <script type="text/javascript" src="js/read_syncs.js"></script>
<script type="text/javascript" src="js/expiring_messages.js"></script> <script type="text/javascript" src="js/expiring_messages.js"></script>
<script type="text/javascript" src="js/chromium.js"></script>
<script type="text/javascript" src="js/registration.js"></script> <script type="text/javascript" src="js/registration.js"></script>
<script type="text/javascript" src="js/views/react_wrapper_view.js"></script> <script type="text/javascript" src="js/views/react_wrapper_view.js"></script>

View File

View File

@ -1,41 +0,0 @@
{
"name": "session-desktop",
"version": "0.0.0",
"homepage": "https://github.com/loki-project/session-desktop",
"license": "GPLV3",
"private": true,
"dependencies": {
"indexeddb-backbonejs-adapter": "*",
"protobuf": "~3.8.0"
},
"devDependencies": {
"mock-socket": "~0.3.2"
},
"preen": {
"indexeddb-backbonejs-adapter": [
"backbone-indexeddb.js"
],
"mock-socket": [
"dist/mock-socket.js"
],
"protobuf": [
"dist/ProtoBuf.js"
]
},
"concat": {
"app": [
"node_modules/jquery/dist/jquery.js",
"node_modules/bytebuffer/dist/bytebuffer.min.js",
"node_modules/long/dist/long.js",
"components/protobuf/**/*.js",
"node_modules/mustache/mustache.js",
"node_modules/underscore/underscore.js",
"node_modules/backbone/backbone.js",
"components/indexeddb-backbonejs-adapter/**/*.js"
],
"libtextsecure": [
"node_modules/long/dist/long.js",
"components/protobuf/**/*.js"
]
}
}

View File

@ -1,662 +0,0 @@
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['backbone', 'underscore'], factory);
} else if (typeof exports === 'object') {
// Node. Does not work with strict CommonJS, but
// only CommonJS-like environments that support module.exports,
// like Node.
module.exports = factory(require('backbone'), require('underscore'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.Backbone, root._);
}
}(this, function (Backbone, _) {
// Generate four random hex digits.
function S4() {
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
}
// Generate a pseudo-GUID by concatenating random hexadecimal.
function guid() {
return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}
if ( _(indexedDB).isUndefined() ) { return; }
// Driver object
// That's the interesting part.
// There is a driver for each schema provided. The schema is a te combination of name (for the database), a version as well as migrations to reach that
// version of the database.
function Driver(schema, ready, nolog, onerror) {
this.schema = schema;
this.ready = ready;
this.error = null;
this.transactions = []; // Used to list all transactions and keep track of active ones.
this.db = null;
this.nolog = nolog;
this.onerror = onerror;
var lastMigrationPathVersion = _.last(this.schema.migrations).version;
if (!this.nolog) debugLog("opening database " + this.schema.id + " in version #" + lastMigrationPathVersion);
this.dbRequest = indexedDB.open(this.schema.id,lastMigrationPathVersion); //schema version need to be an unsigned long
this.launchMigrationPath = function(dbVersion) {
var transaction = this.dbRequest.transaction;
var clonedMigrations = _.clone(schema.migrations);
this.migrate(transaction, clonedMigrations, dbVersion, {
error: function (event) {
this.error = "Database not up to date. " + dbVersion + " expected was " + lastMigrationPathVersion;
}.bind(this)
});
};
this.dbRequest.onblocked = function(event){
if (!this.nolog) debugLog("connection to database blocked");
}
this.dbRequest.onsuccess = function (e) {
this.db = e.target.result; // Attach the connection ot the queue.
var currentIntDBVersion = (parseInt(this.db.version) || 0); // we need convert beacuse chrome store in integer and ie10 DP4+ in int;
var lastMigrationInt = (parseInt(lastMigrationPathVersion) || 0); // And make sure we compare numbers with numbers.
if (currentIntDBVersion === lastMigrationInt) { //if support new event onupgradeneeded will trigger the ready function
// No migration to perform!
this.ready();
} else if (currentIntDBVersion < lastMigrationInt ) {
// We need to migrate up to the current migration defined in the database
this.launchMigrationPath(currentIntDBVersion);
} else {
// Looks like the IndexedDB is at a higher version than the current driver schema.
this.error = "Database version is greater than current code " + currentIntDBVersion + " expected was " + lastMigrationInt;
}
}.bind(this);
this.dbRequest.onerror = function (e) {
// Failed to open the database
this.error = "Couldn't not connect to the database"
if (!this.nolog) debugLog("Couldn't not connect to the database");
this.onerror();
}.bind(this);
this.dbRequest.onabort = function (e) {
// Failed to open the database
this.error = "Connection to the database aborted"
if (!this.nolog) debugLog("Connection to the database aborted");
this.onerror();
}.bind(this);
this.dbRequest.onupgradeneeded = function(iDBVersionChangeEvent){
this.db =iDBVersionChangeEvent.target.result;
var newVersion = iDBVersionChangeEvent.newVersion;
var oldVersion = iDBVersionChangeEvent.oldVersion;
// Fix Safari 8 and iOS 8 bug
// at the first connection oldVersion is equal to 9223372036854776000
// but the real value is 0
if (oldVersion > 99999999999)
oldVersion = 0;
if (!this.nolog) debugLog("onupgradeneeded = " + oldVersion + " => " + newVersion);
this.launchMigrationPath(oldVersion);
}.bind(this);
}
function debugLog(str) {
if (typeof window !== "undefined" && typeof window.console !== "undefined" && typeof window.console.log !== "undefined") {
window.console.log(str);
}
else if(console.log !== "undefined") {
console.log(str)
}
}
// Driver Prototype
Driver.prototype = {
// Tracks transactions. Mostly for debugging purposes. TO-IMPROVE
_track_transaction: function(transaction) {
this.transactions.push(transaction);
function removeIt() {
var idx = this.transactions.indexOf(transaction);
if (idx !== -1) {this.transactions.splice(idx); }
};
transaction.oncomplete = removeIt.bind(this);
transaction.onabort = removeIt.bind(this);
transaction.onerror = removeIt.bind(this);
},
// Performs all the migrations to reach the right version of the database.
migrate: function (transaction, migrations, version, options) {
transaction.onerror = options.error;
transaction.onabort = options.error;
if (!this.nolog) debugLog("migrate begin version from #" + version);
var that = this;
var migration = migrations.shift();
if (migration) {
if (!version || version < migration.version) {
// We need to apply this migration-
if (typeof migration.before == "undefined") {
migration.before = function (next) {
next();
};
}
if (typeof migration.after == "undefined") {
migration.after = function (next) {
next();
};
}
// First, let's run the before script
if (!this.nolog) debugLog("migrate begin before version #" + migration.version);
migration.before(function () {
if (!this.nolog) debugLog("migrate done before version #" + migration.version);
if (!this.nolog) debugLog("migrate begin migrate version #" + migration.version);
migration.migrate(transaction, function () {
if (!this.nolog) debugLog("migrate done migrate version #" + migration.version);
// Migration successfully appliedn let's go to the next one!
if (!this.nolog) debugLog("migrate begin after version #" + migration.version);
migration.after(function () {
if (!this.nolog) debugLog("migrate done after version #" + migration.version);
if (!this.nolog) debugLog("Migrated to " + migration.version);
//last modification occurred, need finish
if(migrations.length ==0) {
if (!this.nolog) {
debugLog("migrate setting transaction.oncomplete to finish version #" + migration.version);
transaction.oncomplete = function() {
debugLog("migrate done transaction.oncomplete version #" + migration.version);
debugLog("Done migrating");
}
}
}
else
{
if (!this.nolog) debugLog("migrate end from version #" + version + " to " + migration.version);
that.migrate(transaction, migrations, version, options);
}
}.bind(this));
}.bind(this));
}.bind(this));
} else {
// No need to apply this migration
if (!this.nolog) debugLog("Skipping migration " + migration.version);
this.migrate(transaction, migrations, version, options);
}
}
},
// This is the main method, called by the ExecutionQueue when the driver is ready (database open and migration performed)
execute: function (storeName, method, object, options) {
if (!this.nolog) debugLog("execute : " + method + " on " + storeName + " for " + object.id);
switch (method) {
case "create":
this.create(storeName, object, options);
break;
case "read":
if (object.id || object.cid) {
this.read(storeName, object, options); // It's a model
} else {
this.query(storeName, object, options); // It's a collection
}
break;
case "update":
this.update(storeName, object, options); // We may want to check that this is not a collection. TOFIX
break;
case "delete":
if (object.id || object.cid) {
this.delete(storeName, object, options);
} else {
this.clear(storeName, object, options);
}
break;
default:
// Hum what?
}
},
// Writes the json to the storeName in db. It is a create operations, which means it will fail if the key already exists
// options are just success and error callbacks.
create: function (storeName, object, options) {
var writeTransaction = this.db.transaction([storeName], 'readwrite');
//this._track_transaction(writeTransaction);
var store = writeTransaction.objectStore(storeName);
var json = object.toJSON();
var idAttribute = _.result(object, 'idAttribute');
var writeRequest;
if (json[idAttribute] === undefined && !store.autoIncrement) json[idAttribute] = guid();
writeTransaction.onerror = function (e) {
options.error(e);
};
writeTransaction.oncomplete = function (e) {
options.success(json);
};
if (!store.keyPath)
writeRequest = store.add(json, json[idAttribute]);
else
writeRequest = store.add(json);
},
// Writes the json to the storeName in db. It is an update operation, which means it will overwrite the value if the key already exist
// options are just success and error callbacks.
update: function (storeName, object, options) {
var writeTransaction = this.db.transaction([storeName], 'readwrite');
//this._track_transaction(writeTransaction);
var store = writeTransaction.objectStore(storeName);
var json = object.toJSON();
var idAttribute = _.result(object, 'idAttribute');
var writeRequest;
if (!json[idAttribute]) json[idAttribute] = guid();
if (!store.keyPath)
writeRequest = store.put(json, json[idAttribute]);
else
writeRequest = store.put(json);
writeRequest.onerror = function (e) {
options.error(e);
};
writeTransaction.oncomplete = function (e) {
options.success(json);
};
},
// Reads from storeName in db with json.id if it's there of with any json.xxxx as long as xxx is an index in storeName
read: function (storeName, object, options) {
var readTransaction = this.db.transaction([storeName], "readonly");
this._track_transaction(readTransaction);
var store = readTransaction.objectStore(storeName);
var json = object.toJSON();
var idAttribute = _.result(object, 'idAttribute');
var getRequest = null;
if (json[idAttribute]) {
getRequest = store.get(json[idAttribute]);
} else if(options.index) {
var index = store.index(options.index.name);
getRequest = index.get(options.index.value);
} else {
// We need to find which index we have
var cardinality = 0; // try to fit the index with most matches
_.each(store.indexNames, function (key, index) {
index = store.index(key);
if(typeof index.keyPath === 'string' && 1 > cardinality) {
// simple index
if (json[index.keyPath] !== undefined) {
getRequest = index.get(json[index.keyPath]);
cardinality = 1;
}
} else if(typeof index.keyPath === 'object' && index.keyPath.length > cardinality) {
// compound index
var valid = true;
var keyValue = _.map(index.keyPath, function(keyPart) {
valid = valid && json[keyPart] !== undefined;
return json[keyPart];
});
if(valid) {
getRequest = index.get(keyValue);
cardinality = index.keyPath.length;
}
}
});
}
if (getRequest) {
getRequest.onsuccess = function (event) {
if (event.target.result) {
options.success(event.target.result);
} else {
options.error("Not Found");
}
};
getRequest.onerror = function () {
options.error("Not Found"); // We couldn't find the record.
}
} else {
options.error("Not Found"); // We couldn't even look for it, as we don't have enough data.
}
},
// Deletes the json.id key and value in storeName from db.
delete: function (storeName, object, options) {
var deleteTransaction = this.db.transaction([storeName], 'readwrite');
//this._track_transaction(deleteTransaction);
var store = deleteTransaction.objectStore(storeName);
var json = object.toJSON();
var idAttribute = _.result(object, 'idAttribute');
var deleteRequest = store.delete(json[idAttribute]);
deleteTransaction.oncomplete = function (event) {
options.success(null);
};
deleteRequest.onerror = function (event) {
options.error("Not Deleted");
};
},
// Clears all records for storeName from db.
clear: function (storeName, object, options) {
var deleteTransaction = this.db.transaction([storeName], "readwrite");
//this._track_transaction(deleteTransaction);
var store = deleteTransaction.objectStore(storeName);
var deleteRequest = store.clear();
deleteRequest.onsuccess = function (event) {
options.success(null);
};
deleteRequest.onerror = function (event) {
options.error("Not Cleared");
};
},
// Performs a query on storeName in db.
// options may include :
// - conditions : value of an index, or range for an index
// - range : range for the primary key
// - limit : max number of elements to be yielded
// - offset : skipped items.
query: function (storeName, collection, options) {
var elements = [];
var skipped = 0, processed = 0;
var queryTransaction = this.db.transaction([storeName], "readonly");
//this._track_transaction(queryTransaction);
var idAttribute = _.result(collection.model.prototype, 'idAttribute');
var readCursor = null;
var store = queryTransaction.objectStore(storeName);
var index = null,
lower = null,
upper = null,
bounds = null;
if (options.conditions) {
// We have a condition, we need to use it for the cursor
_.each(store.indexNames, function (key) {
if (!readCursor) {
index = store.index(key);
if (options.conditions[index.keyPath] instanceof Array) {
lower = options.conditions[index.keyPath][0] > options.conditions[index.keyPath][1] ? options.conditions[index.keyPath][1] : options.conditions[index.keyPath][0];
upper = options.conditions[index.keyPath][0] > options.conditions[index.keyPath][1] ? options.conditions[index.keyPath][0] : options.conditions[index.keyPath][1];
bounds = IDBKeyRange.bound(lower, upper, true, true);
if (options.conditions[index.keyPath][0] > options.conditions[index.keyPath][1]) {
// Looks like we want the DESC order
readCursor = index.openCursor(bounds, window.IDBCursor.PREV || "prev");
} else {
// We want ASC order
readCursor = index.openCursor(bounds, window.IDBCursor.NEXT || "next");
}
} else if (typeof options.conditions[index.keyPath] === 'object' && ('$gt' in options.conditions[index.keyPath] || '$gte' in options.conditions[index.keyPath])) {
if('$gt' in options.conditions[index.keyPath])
bounds = IDBKeyRange.lowerBound(options.conditions[index.keyPath]['$gt'], true);
else
bounds = IDBKeyRange.lowerBound(options.conditions[index.keyPath]['$gte']);
readCursor = index.openCursor(bounds, window.IDBCursor.NEXT || "next");
} else if (typeof options.conditions[index.keyPath] === 'object' && ('$lt' in options.conditions[index.keyPath] || '$lte' in options.conditions[index.keyPath])) {
if('$lt' in options.conditions[index.keyPath])
bounds = IDBKeyRange.upperBound(options.conditions[index.keyPath]['$lt'], true);
else
bounds = IDBKeyRange.upperBound(options.conditions[index.keyPath]['$lte']);
readCursor = index.openCursor(bounds, window.IDBCursor.NEXT || "next");
} else if (options.conditions[index.keyPath] != undefined) {
bounds = IDBKeyRange.only(options.conditions[index.keyPath]);
readCursor = index.openCursor(bounds);
}
}
});
} else if (options.index) {
index = store.index(options.index.name);
var excludeLower = !!options.index.excludeLower;
var excludeUpper = !!options.index.excludeUpper;
if (index) {
if (options.index.lower && options.index.upper) {
bounds = IDBKeyRange.bound(options.index.lower, options.index.upper, excludeLower, excludeUpper);
} else if (options.index.lower) {
bounds = IDBKeyRange.lowerBound(options.index.lower, excludeLower);
} else if (options.index.upper) {
bounds = IDBKeyRange.upperBound(options.index.upper, excludeUpper);
} else if (options.index.only) {
bounds = IDBKeyRange.only(options.index.only);
}
if (typeof options.index.order === 'string' && options.index.order.toLowerCase() === 'desc') {
readCursor = index.openCursor(bounds, window.IDBCursor.PREV || "prev");
} else {
readCursor = index.openCursor(bounds, window.IDBCursor.NEXT || "next");
}
}
} else {
// No conditions, use the index
if (options.range) {
lower = options.range[0] > options.range[1] ? options.range[1] : options.range[0];
upper = options.range[0] > options.range[1] ? options.range[0] : options.range[1];
bounds = IDBKeyRange.bound(lower, upper);
if (options.range[0] > options.range[1]) {
readCursor = store.openCursor(bounds, window.IDBCursor.PREV || "prev");
} else {
readCursor = store.openCursor(bounds, window.IDBCursor.NEXT || "next");
}
} else {
readCursor = store.openCursor();
}
}
if (typeof (readCursor) == "undefined" || !readCursor) {
options.error("No Cursor");
} else {
readCursor.onerror = function(e){
options.error("readCursor error", e);
};
// Setup a handler for the cursors `success` event:
readCursor.onsuccess = function (e) {
var cursor = e.target.result;
if (!cursor) {
if (options.addIndividually || options.clear) {
// nothing!
// We need to indicate that we're done. But, how?
collection.trigger("reset");
} else {
options.success(elements); // We're done. No more elements.
}
}
else {
// Cursor is not over yet.
if (options.limit && processed >= options.limit) {
// Yet, we have processed enough elements. So, let's just skip.
if (bounds) {
if (options.conditions && options.conditions[index.keyPath]) {
cursor.continue(options.conditions[index.keyPath][1] + 1); /* We need to 'terminate' the cursor cleany, by moving to the end */
} else if (options.index && (options.index.upper || options.index.lower)) {
if (typeof options.index.order === 'string' && options.index.order.toLowerCase() === 'desc') {
cursor.continue(options.index.lower);
} else {
cursor.continue(options.index.upper);
}
}
} else {
cursor.continue(); /* We need to 'terminate' the cursor cleany, by moving to the end */
}
}
else if (options.offset && options.offset > skipped) {
skipped++;
cursor.continue(); /* We need to Moving the cursor forward */
} else {
// This time, it looks like it's good!
if (options.addIndividually) {
collection.add(cursor.value);
} else if (options.clear) {
var deleteRequest = store.delete(cursor.value[idAttribute]);
deleteRequest.onsuccess = function (event) {
elements.push(cursor.value);
};
deleteRequest.onerror = function (event) {
elements.push(cursor.value);
};
} else {
elements.push(cursor.value);
}
processed++;
cursor.continue();
}
}
};
}
},
close :function(){
if(this.db){
this.db.close();
}
}
};
// ExecutionQueue object
// The execution queue is an abstraction to buffer up requests to the database.
// It holds a "driver". When the driver is ready, it just fires up the queue and executes in sync.
function ExecutionQueue(schema,next,nolog) {
this.driver = new Driver(schema, this.ready.bind(this), nolog, this.error.bind(this));
this.started = false;
this.failed = false;
this.stack = [];
this.version = _.last(schema.migrations).version;
this.next = next;
}
// ExecutionQueue Prototype
ExecutionQueue.prototype = {
// Called when the driver is ready
// It just loops over the elements in the queue and executes them.
ready: function () {
this.started = true;
_.each(this.stack, function (message) {
this.execute(message);
}.bind(this));
this.stack = []; // fix memory leak
this.next();
},
error: function() {
this.failed = true;
_.each(this.stack, function (message) {
this.execute(message);
}.bind(this));
this.stack = [];
this.next();
},
// Executes a given command on the driver. If not started, just stacks up one more element.
execute: function (message) {
if (this.started) {
try {
this.driver.execute(message[2].storeName || message[1].storeName, message[0], message[1], message[2]); // Upon messages, we execute the query
} catch (e) {
if (e.name === 'InvalidStateError') {
var f = window.onInvalidStateError;
if (f) f(e);
}
throw e;
}
} else if (this.failed) {
message[2].error();
} else {
this.stack.push(message);
}
},
close : function(){
this.driver.close();
}
};
// Method used by Backbone for sync of data with data store. It was initially designed to work with "server side" APIs, This wrapper makes
// it work with the local indexedDB stuff. It uses the schema attribute provided by the object.
// The wrapper keeps an active Executuon Queue for each "schema", and executes querues agains it, based on the object type (collection or
// single model), but also the method... etc.
// Keeps track of the connections
var Databases = {};
function sync(method, object, options) {
if(method == "closeall"){
_.each(Databases,function(database){
database.close();
});
// Clean up active databases object.
Databases = {};
return Backbone.$.Deferred().resolve();
}
// If a model or a collection does not define a database, fall back on ajaxSync
if (!object || !_.isObject(object.database)) {
return Backbone.ajaxSync(method, object, options);
}
var schema = object.database;
if (Databases[schema.id]) {
if(Databases[schema.id].version != _.last(schema.migrations).version){
Databases[schema.id].close();
delete Databases[schema.id];
}
}
var promise;
if (typeof Backbone.$ === 'undefined' || typeof Backbone.$.Deferred === 'undefined') {
var noop = function() {};
var resolve = noop;
var reject = noop;
} else {
var dfd = Backbone.$.Deferred();
var resolve = dfd.resolve;
var reject = dfd.reject;
promise = dfd.promise();
}
var success = options.success;
options.success = function(resp) {
if (success) success(resp);
resolve();
object.trigger('sync', object, resp, options);
};
var error = options.error;
options.error = function(resp) {
if (error) error(resp);
reject();
object.trigger('error', object, resp, options);
};
var next = function(){
Databases[schema.id].execute([method, object, options]);
};
if (!Databases[schema.id]) {
Databases[schema.id] = new ExecutionQueue(schema,next,schema.nolog);
} else {
next();
}
return promise;
};
Backbone.ajaxSync = Backbone.sync;
Backbone.sync = sync;
return { sync: sync, debugLog: debugLog};
}));

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,25 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class='no-js' lang='en'> <html class="no-js" lang="en">
<head> <head>
<meta charset='utf-8'> <meta charset="utf-8" />
<meta content='width=device-width, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0' name='viewport'> <meta
<meta http-equiv="X-UA-Compatible" content="IE=edge"> content="width=device-width, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0"
<title>Session</title> name="viewport"
<meta name="description" content=""> />
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link href='/images/session/session_icon_128.png' rel='shortcut icon'> <title>Session</title>
<link href="/stylesheets/manifest.css" rel="stylesheet" type="text/css" /> <meta name="description" content="" />
<script type="text/javascript" src="js/chromium.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1" />
</head> <link href="/images/session/session_icon_128.png" rel="shortcut icon" />
<body id="signal-container" class='signal index'> <link href="/stylesheets/manifest.css" rel="stylesheet" type="text/css" />
<div class='app-loading-screen'> </head>
<div class="content session-full-logo"> <body id="signal-container" class="signal index">
<img src="images/session/brand.svg" class="session-brand-logo" /> <div class="app-loading-screen">
<img src="images/session/session-text.svg" class="session-text-logo" /> <div class="content session-full-logo">
</div> <img src="images/session/brand.svg" class="session-brand-logo" />
</div> <img src="images/session/session-text.svg" class="session-text-logo" />
<script type="text/javascript" src="js/index.js"></script> </div>
</body> </div>
<script type="text/javascript" src="js/index.js"></script>
</body>
</html> </html>

View File

@ -66,11 +66,6 @@
window.getGlobalOnlineStatus = () => window.globalOnlineStatus; window.getGlobalOnlineStatus = () => window.globalOnlineStatus;
const { Views } = window.Signal; const { Views } = window.Signal;
// Implicitly used in `indexeddb-backbonejs-adapter`:
// https://github.com/signalapp/Signal-Desktop/blob/4033a9f8137e62ed286170ed5d4941982b1d3a64/components/indexeddb-backbonejs-adapter/backbone-indexeddb.js#L569
window.onInvalidStateError = error =>
window.log.error(error && error.stack ? error.stack : error);
window.log.info('background page reloaded'); window.log.info('background page reloaded');
window.log.info('environment:', window.getEnvironment()); window.log.info('environment:', window.getEnvironment());
const restartReason = localStorage.getItem('restart-reason'); const restartReason = localStorage.getItem('restart-reason');
@ -149,15 +144,6 @@
storage.put('spell-check', value); storage.put('spell-check', value);
}, },
addDarkOverlay: () => {
if ($('.dark-overlay').length) {
return;
}
$(document.body).prepend('<div class="dark-overlay"></div>');
$('.dark-overlay').on('click', () => $('.dark-overlay').remove());
},
removeDarkOverlay: () => $('.dark-overlay').remove(),
shutdown: async () => { shutdown: async () => {
// Stop background processing // Stop background processing
window.libsession.Utils.AttachmentDownloads.stop(); window.libsession.Utils.AttachmentDownloads.stop();

View File

@ -1,15 +0,0 @@
/* global extension: false */
// eslint-disable-next-line func-names
(function() {
'use strict';
// Browser specific functions for Chrom*
window.extension = window.extension || {};
extension.windows = {
onClosed(callback) {
window.addEventListener('beforeunload', callback);
},
};
})();

View File

@ -35584,315 +35584,7 @@ var MEMFS = {
}, },
}; };
var IDBFS = {
dbs: {},
indexedDB: function() {
if (typeof indexedDB !== 'undefined') return indexedDB;
var ret = null;
if (typeof window === 'object')
ret =
window.indexedDB ||
window.mozIndexedDB ||
window.webkitIndexedDB ||
window.msIndexedDB;
assert(ret, 'IDBFS used, but indexedDB not supported');
return ret;
},
DB_VERSION: 21,
DB_STORE_NAME: 'FILE_DATA',
mount: function(mount) {
// reuse all of the core MEMFS functionality
return MEMFS.mount.apply(null, arguments);
},
syncfs: function(mount, populate, callback) {
IDBFS.getLocalSet(mount, function(err, local) {
if (err) return callback(err);
IDBFS.getRemoteSet(mount, function(err, remote) {
if (err) return callback(err);
var src = populate ? remote : local;
var dst = populate ? local : remote;
IDBFS.reconcile(src, dst, callback);
});
});
},
getDB: function(name, callback) {
// check the cache first
var db = IDBFS.dbs[name];
if (db) {
return callback(null, db);
}
var req;
try {
req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION);
} catch (e) {
return callback(e);
}
req.onupgradeneeded = function(e) {
var db = e.target.result;
var transaction = e.target.transaction;
var fileStore;
if (db.objectStoreNames.contains(IDBFS.DB_STORE_NAME)) {
fileStore = transaction.objectStore(IDBFS.DB_STORE_NAME);
} else {
fileStore = db.createObjectStore(IDBFS.DB_STORE_NAME);
}
fileStore.createIndex('timestamp', 'timestamp', { unique: false });
};
req.onsuccess = function() {
db = req.result;
// add to the cache
IDBFS.dbs[name] = db;
callback(null, db);
};
req.onerror = function() {
callback(this.error);
};
},
getLocalSet: function(mount, callback) {
var entries = {};
function isRealDir(p) {
return p !== '.' && p !== '..';
}
function toAbsolute(root) {
return function(p) {
return PATH.join2(root, p);
};
}
var check = FS.readdir(mount.mountpoint)
.filter(isRealDir)
.map(toAbsolute(mount.mountpoint));
while (check.length) {
var path = check.pop();
var stat;
try {
stat = FS.stat(path);
} catch (e) {
return callback(e);
}
if (FS.isDir(stat.mode)) {
check.push.apply(
check,
FS.readdir(path)
.filter(isRealDir)
.map(toAbsolute(path))
);
}
entries[path] = { timestamp: stat.mtime };
}
return callback(null, { type: 'local', entries: entries });
},
getRemoteSet: function(mount, callback) {
var entries = {};
IDBFS.getDB(mount.mountpoint, function(err, db) {
if (err) return callback(err);
var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readonly');
transaction.onerror = function() {
callback(this.error);
};
var store = transaction.objectStore(IDBFS.DB_STORE_NAME);
var index = store.index('timestamp');
index.openKeyCursor().onsuccess = function(event) {
var cursor = event.target.result;
if (!cursor) {
return callback(null, { type: 'remote', db: db, entries: entries });
}
entries[cursor.primaryKey] = { timestamp: cursor.key };
cursor.continue();
};
});
},
loadLocalEntry: function(path, callback) {
var stat, node;
try {
var lookup = FS.lookupPath(path);
node = lookup.node;
stat = FS.stat(path);
} catch (e) {
return callback(e);
}
if (FS.isDir(stat.mode)) {
return callback(null, { timestamp: stat.mtime, mode: stat.mode });
} else if (FS.isFile(stat.mode)) {
// Performance consideration: storing a normal JavaScript array to a IndexedDB is much slower than storing a typed array.
// Therefore always convert the file contents to a typed array first before writing the data to IndexedDB.
node.contents = MEMFS.getFileDataAsTypedArray(node);
return callback(null, {
timestamp: stat.mtime,
mode: stat.mode,
contents: node.contents,
});
} else {
return callback(new Error('node type not supported'));
}
},
storeLocalEntry: function(path, entry, callback) {
try {
if (FS.isDir(entry.mode)) {
FS.mkdir(path, entry.mode);
} else if (FS.isFile(entry.mode)) {
FS.writeFile(path, entry.contents, {
encoding: 'binary',
canOwn: true,
});
} else {
return callback(new Error('node type not supported'));
}
FS.chmod(path, entry.mode);
FS.utime(path, entry.timestamp, entry.timestamp);
} catch (e) {
return callback(e);
}
callback(null);
},
removeLocalEntry: function(path, callback) {
try {
var lookup = FS.lookupPath(path);
var stat = FS.stat(path);
if (FS.isDir(stat.mode)) {
FS.rmdir(path);
} else if (FS.isFile(stat.mode)) {
FS.unlink(path);
}
} catch (e) {
return callback(e);
}
callback(null);
},
loadRemoteEntry: function(store, path, callback) {
var req = store.get(path);
req.onsuccess = function(event) {
callback(null, event.target.result);
};
req.onerror = function() {
callback(this.error);
};
},
storeRemoteEntry: function(store, path, entry, callback) {
var req = store.put(entry, path);
req.onsuccess = function() {
callback(null);
};
req.onerror = function() {
callback(this.error);
};
},
removeRemoteEntry: function(store, path, callback) {
var req = store.delete(path);
req.onsuccess = function() {
callback(null);
};
req.onerror = function() {
callback(this.error);
};
},
reconcile: function(src, dst, callback) {
var total = 0;
var create = [];
Object.keys(src.entries).forEach(function(key) {
var e = src.entries[key];
var e2 = dst.entries[key];
if (!e2 || e.timestamp > e2.timestamp) {
create.push(key);
total++;
}
});
var remove = [];
Object.keys(dst.entries).forEach(function(key) {
var e = dst.entries[key];
var e2 = src.entries[key];
if (!e2) {
remove.push(key);
total++;
}
});
if (!total) {
return callback(null);
}
var errored = false;
var completed = 0;
var db = src.type === 'remote' ? src.db : dst.db;
var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite');
var store = transaction.objectStore(IDBFS.DB_STORE_NAME);
function done(err) {
if (err) {
if (!done.errored) {
done.errored = true;
return callback(err);
}
return;
}
if (++completed >= total) {
return callback(null);
}
}
transaction.onerror = function() {
done(this.error);
};
// sort paths in ascending order so directory entries are created
// before the files inside them
create.sort().forEach(function(path) {
if (dst.type === 'local') {
IDBFS.loadRemoteEntry(store, path, function(err, entry) {
if (err) return done(err);
IDBFS.storeLocalEntry(path, entry, done);
});
} else {
IDBFS.loadLocalEntry(path, function(err, entry) {
if (err) return done(err);
IDBFS.storeRemoteEntry(store, path, entry, done);
});
}
});
// sort paths in descending order so files are deleted before their
// parent directories
remove
.sort()
.reverse()
.forEach(function(path) {
if (dst.type === 'local') {
IDBFS.removeLocalEntry(path, done);
} else {
IDBFS.removeRemoteEntry(store, path, done);
}
});
},
};
var NODEFS = { var NODEFS = {
isWindows: false, isWindows: false,
@ -38010,111 +37702,6 @@ var FS = {
processData(url); processData(url);
} }
}, },
indexedDB: function() {
return (
window.indexedDB ||
window.mozIndexedDB ||
window.webkitIndexedDB ||
window.msIndexedDB
);
},
DB_NAME: function() {
return 'EM_FS_' + window.location.pathname;
},
DB_VERSION: 20,
DB_STORE_NAME: 'FILE_DATA',
saveFilesToDB: function(paths, onload, onerror) {
onload = onload || function() {};
onerror = onerror || function() {};
var indexedDB = FS.indexedDB();
try {
var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
} catch (e) {
return onerror(e);
}
openRequest.onupgradeneeded = function openRequest_onupgradeneeded() {
console.log('creating db');
var db = openRequest.result;
db.createObjectStore(FS.DB_STORE_NAME);
};
openRequest.onsuccess = function openRequest_onsuccess() {
var db = openRequest.result;
var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite');
var files = transaction.objectStore(FS.DB_STORE_NAME);
var ok = 0,
fail = 0,
total = paths.length;
function finish() {
if (fail == 0) onload();
else onerror();
}
paths.forEach(function(path) {
var putRequest = files.put(FS.analyzePath(path).object.contents, path);
putRequest.onsuccess = function putRequest_onsuccess() {
ok++;
if (ok + fail == total) finish();
};
putRequest.onerror = function putRequest_onerror() {
fail++;
if (ok + fail == total) finish();
};
});
transaction.onerror = onerror;
};
openRequest.onerror = onerror;
},
loadFilesFromDB: function(paths, onload, onerror) {
onload = onload || function() {};
onerror = onerror || function() {};
var indexedDB = FS.indexedDB();
try {
var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
} catch (e) {
return onerror(e);
}
openRequest.onupgradeneeded = onerror; // no database to load from
openRequest.onsuccess = function openRequest_onsuccess() {
var db = openRequest.result;
try {
var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly');
} catch (e) {
onerror(e);
return;
}
var files = transaction.objectStore(FS.DB_STORE_NAME);
var ok = 0,
fail = 0,
total = paths.length;
function finish() {
if (fail == 0) onload();
else onerror();
}
paths.forEach(function(path) {
var getRequest = files.get(path);
getRequest.onsuccess = function getRequest_onsuccess() {
if (FS.analyzePath(path).exists) {
FS.unlink(path);
}
FS.createDataFile(
PATH.dirname(path),
PATH.basename(path),
getRequest.result,
true,
true,
true
);
ok++;
if (ok + fail == total) finish();
};
getRequest.onerror = function getRequest_onerror() {
fail++;
if (ok + fail == total) finish();
};
});
transaction.onerror = onerror;
};
openRequest.onerror = onerror;
},
}; };
var PATH = { var PATH = {
splitPath: function(filename) { splitPath: function(filename) {

View File

@ -1,122 +0,0 @@
/* global _: false */
/* global Backbone: false */
/* global Whisper: false */
// eslint-disable-next-line func-names
(function() {
'use strict';
window.Whisper = window.Whisper || {};
window.Whisper.Database = window.Whisper.Database || {};
window.Whisper.Database.id = window.Whisper.Database.id || 'loki-messenger';
window.Whisper.Database.nolog = true;
Whisper.Database.handleDOMException = (prefix, error, reject) => {
window.log.error(
`${prefix}:`,
error && error.name,
error && error.message,
error && error.code
);
reject(error || new Error(prefix));
};
function clearStores(db, names) {
return new Promise((resolve, reject) => {
const storeNames = names || db.objectStoreNames;
window.log.info('Clearing these indexeddb stores:', storeNames);
const transaction = db.transaction(storeNames, 'readwrite');
let finished = false;
const finish = via => {
window.log.info('clearing all stores done via', via);
if (finished) {
resolve();
}
finished = true;
};
transaction.oncomplete = finish.bind(null, 'transaction complete');
transaction.onerror = () => {
Whisper.Database.handleDOMException(
'clearStores transaction error',
transaction.error,
reject
);
};
let count = 0;
// can't use built-in .forEach because db.objectStoreNames is not a plain array
_.forEach(storeNames, storeName => {
const store = transaction.objectStore(storeName);
const request = store.clear();
request.onsuccess = () => {
count += 1;
window.log.info('Done clearing store', storeName);
if (count >= storeNames.length) {
window.log.info('Done clearing indexeddb stores');
finish('clears complete');
}
};
request.onerror = () => {
Whisper.Database.handleDOMException('clearStores request error', request.error, reject);
};
});
});
}
Whisper.Database.open = () => {
const { migrations } = Whisper.Database;
const { version } = migrations[migrations.length - 1];
const DBOpenRequest = window.indexedDB.open(Whisper.Database.id, version);
return new Promise((resolve, reject) => {
// these two event handlers act on the IDBDatabase object,
// when the database is opened successfully, or not
DBOpenRequest.onerror = reject;
DBOpenRequest.onsuccess = () => resolve(DBOpenRequest.result);
// This event handles the event whereby a new version of
// the database needs to be created Either one has not
// been created before, or a new version number has been
// submitted via the window.indexedDB.open line above
DBOpenRequest.onupgradeneeded = reject;
});
};
Whisper.Database.clear = async () => {
const db = await Whisper.Database.open();
await clearStores(db);
db.close();
};
Whisper.Database.clearStores = async storeNames => {
const db = await Whisper.Database.open();
await clearStores(db, storeNames);
db.close();
};
Whisper.Database.close = () => window.wrapDeferred(Backbone.sync('closeall'));
Whisper.Database.drop = () =>
new Promise((resolve, reject) => {
const request = window.indexedDB.deleteDatabase(Whisper.Database.id);
request.onblocked = () => {
reject(new Error('Error deleting database: Blocked.'));
};
request.onupgradeneeded = () => {
reject(new Error('Error deleting database: Upgrade needed.'));
};
request.onerror = () => {
reject(new Error('Error deleting database.'));
};
request.onsuccess = resolve;
});
})();

View File

@ -1,89 +0,0 @@
/* global Backbone, Whisper */
/* eslint-disable more/no-then */
// eslint-disable-next-line func-names
(function() {
'use strict';
window.Whisper = window.Whisper || {};
const Item = Backbone.Model.extend({
database: Whisper.Database,
storeName: 'items',
});
const ItemCollection = Backbone.Collection.extend({
model: Item,
storeName: 'items',
database: Whisper.Database,
});
let ready = false;
const items = new ItemCollection();
items.on('reset', () => {
ready = true;
});
window.legacyStorage = {
/** ***************************
*** Base Storage Routines ***
**************************** */
put(key, value) {
if (value === undefined) {
throw new Error('Tried to store undefined');
}
if (!ready) {
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) => {
item.save().then(resolve, reject);
});
},
get(key, defaultValue) {
const item = items.get(`${key}`);
if (!item) {
return defaultValue;
}
return item.get('value');
},
remove(key) {
const item = items.get(`${key}`);
if (item) {
items.remove(item);
return new Promise((resolve, reject) => {
item.destroy().then(resolve, reject);
});
}
return Promise.resolve();
},
onready(callback) {
if (ready) {
callback();
} else {
items.on('reset', callback);
}
},
fetch() {
return new Promise((resolve, reject) => {
items
.fetch({ reset: true })
.fail(() =>
reject(
new Error(
'Failed to fetch from storage.' +
' This may be due to an unexpected database version.'
)
)
)
.always(resolve);
});
},
reset() {
items.reset();
},
};
})();

View File

View File

@ -3,13 +3,13 @@
/* eslint strict: ['error', 'never'] */ /* eslint strict: ['error', 'never'] */
/* eslint-disable no-console */ /* eslint-disable no-console */
const electron = require('electron'); const { ipcRenderer } = require('electron');
const _ = require('lodash'); const _ = require('lodash');
const debuglogs = require('./modules/debuglogs'); const debuglogs = require('./modules/debuglogs');
const Privacy = require('./modules/privacy'); const Privacy = require('./modules/privacy');
const ipc = electron.ipcRenderer; const ipc = ipcRenderer;
// Default Bunyan levels: https://github.com/trentm/node-bunyan#levels // Default Bunyan levels: https://github.com/trentm/node-bunyan#levels
// To make it easier to visually scan logs, we make all levels the same length // To make it easier to visually scan logs, we make all levels the same length

View File

@ -1,65 +0,0 @@
/* global indexedDB */
// Module for interacting with IndexedDB without Backbone IndexedDB adapter
// and using promises. Revisit use of `idb` dependency as it might cover
// this functionality.
const { isObject, isNumber } = require('lodash');
exports.open = (name, version, { onUpgradeNeeded } = {}) => {
const request = indexedDB.open(name, version);
return new Promise((resolve, reject) => {
request.onblocked = () => reject(new Error('Database blocked'));
request.onupgradeneeded = event => {
const hasRequestedSpecificVersion = isNumber(version);
if (!hasRequestedSpecificVersion) {
return;
}
const { newVersion, oldVersion } = event;
if (onUpgradeNeeded) {
const { transaction } = event.target;
onUpgradeNeeded({ oldVersion, transaction });
return;
}
reject(
new Error(`Database upgrade required: oldVersion: ${oldVersion}, newVersion: ${newVersion}`)
);
};
request.onerror = event => reject(event.target.error);
request.onsuccess = event => {
const connection = event.target.result;
resolve(connection);
};
});
};
exports.completeTransaction = transaction =>
new Promise((resolve, reject) => {
transaction.addEventListener('abort', event => reject(event.target.error));
transaction.addEventListener('error', event => reject(event.target.error));
transaction.addEventListener('complete', () => resolve());
});
exports.getVersion = async name => {
const connection = await exports.open(name);
const { version } = connection;
connection.close();
return version;
};
exports.getCount = async ({ store } = {}) => {
if (!isObject(store)) {
throw new TypeError("'store' is required");
}
const request = store.count();
return new Promise((resolve, reject) => {
request.onerror = event => reject(event.target.error);
request.onsuccess = event => resolve(event.target.result);
});
};

View File

@ -1,24 +0,0 @@
/* eslint-disable more/no-then */
class JobQueue {
constructor() {
this.pending = Promise.resolve();
}
add(job) {
const previous = this.pending || Promise.resolve();
this.pending = previous.then(job, job);
const current = this.pending;
current.then(() => {
if (this.pending === current) {
delete this.pending;
}
});
return current;
}
}
module.exports = {
JobQueue,
};

View File

@ -2,7 +2,6 @@
const Crypto = require('./crypto'); const Crypto = require('./crypto');
const Data = require('../../ts/data/data'); const Data = require('../../ts/data/data');
const Database = require('./database');
const Emoji = require('../../ts/util/emoji'); const Emoji = require('../../ts/util/emoji');
const Notifications = require('../../ts/notifications'); const Notifications = require('../../ts/notifications');
const OS = require('../../ts/OS'); const OS = require('../../ts/OS');
@ -143,7 +142,6 @@ exports.setup = (options = {}) => {
Components, Components,
Crypto, Crypto,
Data, Data,
Database,
Emoji, Emoji,
LinkPreviews, LinkPreviews,
Migrations, Migrations,

View File

@ -1,3 +0,0 @@
/* global setTimeout */
exports.sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

View File

@ -48,7 +48,6 @@
} }
}, },
openStandalone() { openStandalone() {
window.addSetupMenuItems();
this.resetViews(); this.resetViews();
this.standaloneView = new Whisper.SessionRegistrationView(); this.standaloneView = new Whisper.SessionRegistrationView();
this.openView(this.standaloneView); this.openView(this.standaloneView);

View File

@ -1,82 +0,0 @@
/* global window, Event, textsecure */
/*
* Implements EventTarget
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget
*/
// eslint-disable-next-line func-names
(function() {
window.textsecure = window.textsecure || {};
function EventTarget() {}
EventTarget.prototype = {
constructor: EventTarget,
dispatchEvent(ev) {
if (!(ev instanceof Event)) {
throw new Error('Expects an event');
}
if (this.listeners === null || typeof this.listeners !== 'object') {
this.listeners = {};
}
const listeners = this.listeners[ev.type];
const results = [];
if (typeof listeners === 'object') {
for (let i = 0, max = listeners.length; i < max; i += 1) {
const listener = listeners[i];
if (typeof listener === 'function') {
results.push(listener.call(null, ev));
}
}
}
return results;
},
addEventListener(eventName, callback) {
if (typeof eventName !== 'string') {
throw new Error('First argument expects a string');
}
if (typeof callback !== 'function') {
throw new Error('Second argument expects a function');
}
if (this.listeners === null || typeof this.listeners !== 'object') {
this.listeners = {};
}
let listeners = this.listeners[eventName];
if (typeof listeners !== 'object') {
listeners = [];
}
listeners.push(callback);
this.listeners[eventName] = listeners;
},
removeEventListener(eventName, callback) {
if (typeof eventName !== 'string') {
throw new Error('First argument expects a string');
}
if (typeof callback !== 'function') {
throw new Error('Second argument expects a function');
}
if (this.listeners === null || typeof this.listeners !== 'object') {
this.listeners = {};
}
const listeners = this.listeners[eventName];
if (typeof listeners === 'object') {
for (let i = 0; i < listeners.length; i += 1) {
if (listeners[i] === callback) {
listeners.splice(i, 1);
return;
}
}
}
this.listeners[eventName] = listeners;
},
extend(obj) {
// eslint-disable-next-line no-restricted-syntax, guard-for-in
for (const prop in obj) {
this[prop] = obj[prop];
}
return this;
},
};
textsecure.EventTarget = EventTarget;
})();

View File

@ -2038,263 +2038,6 @@ var libsignal
} }
}; };
var IDBFS = {
dbs: {}, indexedDB: function () {
if (typeof indexedDB !== 'undefined') return indexedDB;
var ret = null;
if (typeof window === 'object') ret = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
assert(ret, 'IDBFS used, but indexedDB not supported');
return ret;
}, DB_VERSION: 21, DB_STORE_NAME: "FILE_DATA", mount: function (mount) {
// reuse all of the core MEMFS functionality
return MEMFS.mount.apply(null, arguments);
}, syncfs: function (mount, populate, callback) {
IDBFS.getLocalSet(mount, function (err, local) {
if (err) return callback(err);
IDBFS.getRemoteSet(mount, function (err, remote) {
if (err) return callback(err);
var src = populate ? remote : local;
var dst = populate ? local : remote;
IDBFS.reconcile(src, dst, callback);
});
});
}, getDB: function (name, callback) {
// check the cache first
var db = IDBFS.dbs[name];
if (db) {
return callback(null, db);
}
var req;
try {
req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION);
} catch (e) {
return callback(e);
}
req.onupgradeneeded = function (e) {
var db = e.target.result;
var transaction = e.target.transaction;
var fileStore;
if (db.objectStoreNames.contains(IDBFS.DB_STORE_NAME)) {
fileStore = transaction.objectStore(IDBFS.DB_STORE_NAME);
} else {
fileStore = db.createObjectStore(IDBFS.DB_STORE_NAME);
}
fileStore.createIndex('timestamp', 'timestamp', { unique: false });
};
req.onsuccess = function () {
db = req.result;
// add to the cache
IDBFS.dbs[name] = db;
callback(null, db);
};
req.onerror = function () {
callback(this.error);
};
}, getLocalSet: function (mount, callback) {
var entries = {};
function isRealDir(p) {
return p !== '.' && p !== '..';
};
function toAbsolute(root) {
return function (p) {
return PATH.join2(root, p);
}
};
var check = FS.readdir(mount.mountpoint).filter(isRealDir).map(toAbsolute(mount.mountpoint));
while (check.length) {
var path = check.pop();
var stat;
try {
stat = FS.stat(path);
} catch (e) {
return callback(e);
}
if (FS.isDir(stat.mode)) {
check.push.apply(check, FS.readdir(path).filter(isRealDir).map(toAbsolute(path)));
}
entries[path] = { timestamp: stat.mtime };
}
return callback(null, { type: 'local', entries: entries });
}, getRemoteSet: function (mount, callback) {
var entries = {};
IDBFS.getDB(mount.mountpoint, function (err, db) {
if (err) return callback(err);
var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readonly');
transaction.onerror = function () { callback(this.error); };
var store = transaction.objectStore(IDBFS.DB_STORE_NAME);
var index = store.index('timestamp');
index.openKeyCursor().onsuccess = function (event) {
var cursor = event.target.result;
if (!cursor) {
return callback(null, { type: 'remote', db: db, entries: entries });
}
entries[cursor.primaryKey] = { timestamp: cursor.key };
cursor.continue();
};
});
}, loadLocalEntry: function (path, callback) {
var stat, node;
try {
var lookup = FS.lookupPath(path);
node = lookup.node;
stat = FS.stat(path);
} catch (e) {
return callback(e);
}
if (FS.isDir(stat.mode)) {
return callback(null, { timestamp: stat.mtime, mode: stat.mode });
} else if (FS.isFile(stat.mode)) {
// Performance consideration: storing a normal JavaScript array to a IndexedDB is much slower than storing a typed array.
// Therefore always convert the file contents to a typed array first before writing the data to IndexedDB.
node.contents = MEMFS.getFileDataAsTypedArray(node);
return callback(null, { timestamp: stat.mtime, mode: stat.mode, contents: node.contents });
} else {
return callback(new Error('node type not supported'));
}
}, storeLocalEntry: function (path, entry, callback) {
try {
if (FS.isDir(entry.mode)) {
FS.mkdir(path, entry.mode);
} else if (FS.isFile(entry.mode)) {
FS.writeFile(path, entry.contents, { encoding: 'binary', canOwn: true });
} else {
return callback(new Error('node type not supported'));
}
FS.chmod(path, entry.mode);
FS.utime(path, entry.timestamp, entry.timestamp);
} catch (e) {
return callback(e);
}
callback(null);
}, removeLocalEntry: function (path, callback) {
try {
var lookup = FS.lookupPath(path);
var stat = FS.stat(path);
if (FS.isDir(stat.mode)) {
FS.rmdir(path);
} else if (FS.isFile(stat.mode)) {
FS.unlink(path);
}
} catch (e) {
return callback(e);
}
callback(null);
}, loadRemoteEntry: function (store, path, callback) {
var req = store.get(path);
req.onsuccess = function (event) { callback(null, event.target.result); };
req.onerror = function () { callback(this.error); };
}, storeRemoteEntry: function (store, path, entry, callback) {
var req = store.put(entry, path);
req.onsuccess = function () { callback(null); };
req.onerror = function () { callback(this.error); };
}, removeRemoteEntry: function (store, path, callback) {
var req = store.delete(path);
req.onsuccess = function () { callback(null); };
req.onerror = function () { callback(this.error); };
}, reconcile: function (src, dst, callback) {
var total = 0;
var create = [];
Object.keys(src.entries).forEach(function (key) {
var e = src.entries[key];
var e2 = dst.entries[key];
if (!e2 || e.timestamp > e2.timestamp) {
create.push(key);
total++;
}
});
var remove = [];
Object.keys(dst.entries).forEach(function (key) {
var e = dst.entries[key];
var e2 = src.entries[key];
if (!e2) {
remove.push(key);
total++;
}
});
if (!total) {
return callback(null);
}
var errored = false;
var completed = 0;
var db = src.type === 'remote' ? src.db : dst.db;
var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite');
var store = transaction.objectStore(IDBFS.DB_STORE_NAME);
function done(err) {
if (err) {
if (!done.errored) {
done.errored = true;
return callback(err);
}
return;
}
if (++completed >= total) {
return callback(null);
}
};
transaction.onerror = function () { done(this.error); };
// sort paths in ascending order so directory entries are created
// before the files inside them
create.sort().forEach(function (path) {
if (dst.type === 'local') {
IDBFS.loadRemoteEntry(store, path, function (err, entry) {
if (err) return done(err);
IDBFS.storeLocalEntry(path, entry, done);
});
} else {
IDBFS.loadLocalEntry(path, function (err, entry) {
if (err) return done(err);
IDBFS.storeRemoteEntry(store, path, entry, done);
});
}
});
// sort paths in descending order so files are deleted before their
// parent directories
remove.sort().reverse().forEach(function (path) {
if (dst.type === 'local') {
IDBFS.removeLocalEntry(path, done);
} else {
IDBFS.removeRemoteEntry(store, path, done);
}
});
}
};
var NODEFS = { var NODEFS = {
isWindows: false, staticInit: function () { isWindows: false, staticInit: function () {
NODEFS.isWindows = !!process.platform.match(/^win/); NODEFS.isWindows = !!process.platform.match(/^win/);
@ -4049,79 +3792,7 @@ var libsignal
} else { } else {
processData(url); processData(url);
} }
}, indexedDB: function () { },
return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
}, DB_NAME: function () {
return 'EM_FS_' + window.location.pathname;
}, DB_VERSION: 20, DB_STORE_NAME: "FILE_DATA", saveFilesToDB: function (paths, onload, onerror) {
onload = onload || function () { };
onerror = onerror || function () { };
var indexedDB = FS.indexedDB();
try {
var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
} catch (e) {
return onerror(e);
}
openRequest.onupgradeneeded = function openRequest_onupgradeneeded() {
console.log('creating db');
var db = openRequest.result;
db.createObjectStore(FS.DB_STORE_NAME);
};
openRequest.onsuccess = function openRequest_onsuccess() {
var db = openRequest.result;
var transaction = db.transaction([FS.DB_STORE_NAME], 'readwrite');
var files = transaction.objectStore(FS.DB_STORE_NAME);
var ok = 0, fail = 0, total = paths.length;
function finish() {
if (fail == 0) onload(); else onerror();
}
paths.forEach(function (path) {
var putRequest = files.put(FS.analyzePath(path).object.contents, path);
putRequest.onsuccess = function putRequest_onsuccess() { ok++; if (ok + fail == total) finish() };
putRequest.onerror = function putRequest_onerror() { fail++; if (ok + fail == total) finish() };
});
transaction.onerror = onerror;
};
openRequest.onerror = onerror;
}, loadFilesFromDB: function (paths, onload, onerror) {
onload = onload || function () { };
onerror = onerror || function () { };
var indexedDB = FS.indexedDB();
try {
var openRequest = indexedDB.open(FS.DB_NAME(), FS.DB_VERSION);
} catch (e) {
return onerror(e);
}
openRequest.onupgradeneeded = onerror; // no database to load from
openRequest.onsuccess = function openRequest_onsuccess() {
var db = openRequest.result;
try {
var transaction = db.transaction([FS.DB_STORE_NAME], 'readonly');
} catch (e) {
onerror(e);
return;
}
var files = transaction.objectStore(FS.DB_STORE_NAME);
var ok = 0, fail = 0, total = paths.length;
function finish() {
if (fail == 0) onload(); else onerror();
}
paths.forEach(function (path) {
var getRequest = files.get(path);
getRequest.onsuccess = function getRequest_onsuccess() {
if (FS.analyzePath(path).exists) {
FS.unlink(path);
}
FS.createDataFile(PATH.dirname(path), PATH.basename(path), getRequest.result, true, true, true);
ok++;
if (ok + fail == total) finish();
};
getRequest.onerror = function getRequest_onerror() { fail++; if (ok + fail == total) finish() };
});
transaction.onerror = onerror;
};
openRequest.onerror = onerror;
}
}; var PATH = { }; var PATH = {
splitPath: function (filename) { splitPath: function (filename) {
var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;

View File

@ -1,37 +0,0 @@
/* global window, dcodeIO, textsecure */
// eslint-disable-next-line func-names
(function() {
window.textsecure = window.textsecure || {};
window.textsecure.protobuf = {};
function loadProtoBufs(filename) {
return dcodeIO.ProtoBuf.loadProtoFile(
{ 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
}`;
window.log.error(text);
throw error;
}
const protos = result.build('signalservice');
if (!protos) {
const text = `Error loading protos from ${filename} (root: ${window.PROTO_ROOT})`;
window.log.error(text);
throw new Error(text);
}
// eslint-disable-next-line no-restricted-syntax, guard-for-in
for (const protoName in protos) {
textsecure.protobuf[protoName] = protos[protoName];
}
}
);
}
// this is all the Session Protocols
loadProtoBufs('SignalService.proto');
// this is for websocket wrapping of messages
loadProtoBufs('SubProtocol.proto');
})();

View File

@ -1,100 +0,0 @@
/* global window, StringView */
/* eslint-disable no-bitwise, no-nested-ternary, */
// eslint-disable-next-line func-names
(function() {
window.StringView = {
/*
* These functions from the Mozilla Developer Network
* and have been placed in the public domain.
* https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
* https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses
*/
b64ToUint6(nChr) {
return nChr > 64 && nChr < 91
? nChr - 65
: nChr > 96 && nChr < 123
? nChr - 71
: nChr > 47 && nChr < 58
? nChr + 4
: nChr === 43
? 62
: nChr === 47
? 63
: 0;
},
// This is not a "standard" base64, do not use!
base64ToBytes(sBase64, nBlocksSize) {
const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, '');
const nInLen = sB64Enc.length;
const nOutLen = nBlocksSize
? Math.ceil(((nInLen * 3 + 1) >> 2) / nBlocksSize) * nBlocksSize
: (nInLen * 3 + 1) >> 2;
const aBBytes = new ArrayBuffer(nOutLen);
const taBytes = new Uint8Array(aBBytes);
let nMod3;
let nMod4;
for (let nUint24 = 0, nOutIdx = 0, nInIdx = 0; nInIdx < nInLen; nInIdx += 1) {
nMod4 = nInIdx & 3;
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) {
taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255;
}
nUint24 = 0;
}
}
return aBBytes;
},
uint6ToB64(nUint6) {
return nUint6 < 26
? nUint6 + 65
: nUint6 < 52
? nUint6 + 71
: nUint6 < 62
? nUint6 - 4
: nUint6 === 62
? 43
: nUint6 === 63
? 47
: 65;
},
bytesToBase64(aBytes) {
let nMod3;
let sB64Enc = '';
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';
}
nUint24 |= aBytes[nIdx] << ((16 >>> nMod3) & 24);
if (nMod3 === 2 || aBytes.length - nIdx === 1) {
sB64Enc += String.fromCharCode(
StringView.uint6ToB64((nUint24 >>> 18) & 63),
StringView.uint6ToB64((nUint24 >>> 12) & 63),
StringView.uint6ToB64((nUint24 >>> 6) & 63),
StringView.uint6ToB64(nUint24 & 63)
);
nUint24 = 0;
}
}
return sB64Enc.replace(/A(?=A$|$)/g, '=');
},
arrayBufferToHex(aArrayBuffer) {
return Array.prototype.map
.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;
},
};
})();

23
main.js
View File

@ -597,12 +597,10 @@ async function showDebugLogWindow() {
debugLogWindow.loadURL(prepareURL([__dirname, 'debug_log.html'], { theme })); debugLogWindow.loadURL(prepareURL([__dirname, 'debug_log.html'], { theme }));
debugLogWindow.on('closed', () => { debugLogWindow.on('closed', () => {
removeDarkOverlay();
debugLogWindow = null; debugLogWindow = null;
}); });
debugLogWindow.once('ready-to-show', () => { debugLogWindow.once('ready-to-show', () => {
addDarkOverlay();
debugLogWindow.show(); debugLogWindow.show();
}); });
} }
@ -820,14 +818,6 @@ ipc.on('locale-data', event => {
event.returnValue = locale.messages; event.returnValue = locale.messages;
}); });
ipc.on('set-badge-count', (event, count) => {
app.setBadgeCount(count);
});
ipc.on('remove-setup-menu-items', () => {
setupMenu();
});
ipc.on('add-setup-menu-items', () => { ipc.on('add-setup-menu-items', () => {
setupMenu({ setupMenu({
includeSetup: false, includeSetup: false,
@ -957,19 +947,6 @@ ipc.on('close-debug-log', () => {
} }
}); });
// Settings-related IPC calls
function addDarkOverlay() {
if (mainWindow && mainWindow.webContents) {
mainWindow.webContents.send('add-dark-overlay');
}
}
function removeDarkOverlay() {
if (mainWindow && mainWindow.webContents) {
mainWindow.webContents.send('remove-dark-overlay');
}
}
// This should be called with an ipc sendSync // This should be called with an ipc sendSync
ipc.on('get-media-permissions', event => { ipc.on('get-media-permissions', event => {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign

View File

View File

@ -175,7 +175,6 @@
"@types/webpack": "^5.28.0", "@types/webpack": "^5.28.0",
"arraybuffer-loader": "1.0.3", "arraybuffer-loader": "1.0.3",
"asar": "0.14.0", "asar": "0.14.0",
"bower": "1.8.2",
"chai": "4.3.4", "chai": "4.3.4",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"chai-bytes": "^0.1.2", "chai-bytes": "^0.1.2",

View File

@ -2,18 +2,12 @@
/* global Whisper: false */ /* global Whisper: false */
/* global window: false */ /* global window: false */
const path = require('path'); const path = require('path');
const electron = require('electron'); const { webFrame, remote, clipboard, ipcRenderer } = require('electron');
const { webFrame } = electron;
const semver = require('semver'); const semver = require('semver');
const { deferredToPromise } = require('./js/modules/deferred_to_promise'); const { deferredToPromise } = require('./js/modules/deferred_to_promise');
const { JobQueue } = require('./js/modules/job_queue');
const { app } = electron.remote; const { app } = remote;
const { clipboard } = electron;
window.PROTO_ROOT = 'protos';
const config = require('url').parse(window.location.toString(), true).query; const config = require('url').parse(window.location.toString(), true).query;
@ -43,7 +37,6 @@ window.getCommitHash = () => config.commitHash;
window.getNodeVersion = () => config.node_version; window.getNodeVersion = () => config.node_version;
window.getHostName = () => config.hostname; window.getHostName = () => config.hostname;
window.getServerTrustRoot = () => config.serverTrustRoot; window.getServerTrustRoot = () => config.serverTrustRoot;
window.JobQueue = JobQueue;
window.isBehindProxy = () => Boolean(config.proxyUrl); window.isBehindProxy = () => Boolean(config.proxyUrl);
window.lokiFeatureFlags = { window.lokiFeatureFlags = {
@ -83,7 +76,7 @@ window.versionInfo = {
window.wrapDeferred = deferredToPromise; window.wrapDeferred = deferredToPromise;
const ipc = electron.ipcRenderer; const ipc = ipcRenderer;
const localeMessages = ipc.sendSync('locale-data'); const localeMessages = ipc.sendSync('locale-data');
window.updateZoomFactor = () => { window.updateZoomFactor = () => {
@ -99,8 +92,6 @@ window.getZoomFactor = () => {
webFrame.getZoomFactor(); webFrame.getZoomFactor();
}; };
window.setBadgeCount = count => ipc.send('set-badge-count', count);
// Set the password for the database // Set the password for the database
window.setPassword = (passPhrase, oldPhrase) => window.setPassword = (passPhrase, oldPhrase) =>
new Promise((resolve, reject) => { new Promise((resolve, reject) => {
@ -170,21 +161,6 @@ ipc.on('get-theme-setting', () => {
ipc.send('get-success-theme-setting', theme); ipc.send('get-success-theme-setting', theme);
}); });
// Settings-related events
ipc.on('add-dark-overlay', () => {
const { addDarkOverlay } = window.Events;
if (addDarkOverlay) {
addDarkOverlay();
}
});
ipc.on('remove-dark-overlay', () => {
const { removeDarkOverlay } = window.Events;
if (removeDarkOverlay) {
removeDarkOverlay();
}
});
window.getSettingValue = (settingID, comparisonValue = null) => { window.getSettingValue = (settingID, comparisonValue = null) => {
// Comparison value allows you to pull boolean values from any type. // Comparison value allows you to pull boolean values from any type.
// Eg. window.getSettingValue('theme', 'light') // Eg. window.getSettingValue('theme', 'light')
@ -242,9 +218,6 @@ ipc.on('get-ready-for-shutdown', async () => {
} }
}); });
window.addSetupMenuItems = () => ipc.send('add-setup-menu-items');
window.removeSetupMenuItems = () => ipc.send('remove-setup-menu-items');
// We pull these dependencies in now, from here, because they have Node.js dependencies // We pull these dependencies in now, from here, because they have Node.js dependencies
require('./js/logging'); require('./js/logging');

View File

@ -37,17 +37,6 @@ audio {
max-width: 100%; max-width: 100%;
} }
.dark-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: $color-black;
opacity: 0.25;
z-index: 200;
}
.clearfix:before, .clearfix:before,
.clearfix:after { .clearfix:after {
display: table; display: table;

View File

@ -33,11 +33,6 @@
} }
// _global // _global
.dark-overlay {
background-color: $color-gray-95;
}
.title-bar { .title-bar {
color: $color-dark-05; color: $color-dark-05;
} }

View File

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import Electron from 'electron'; import { shell } from 'electron';
const { shell } = Electron;
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch, useSelector } from 'react-redux';

View File

@ -323,29 +323,33 @@ export function getMarkAllReadMenuItem(conversationId: string): JSX.Element | nu
} }
export function getStartCallMenuItem(conversationId: string): JSX.Element | null { export function getStartCallMenuItem(conversationId: string): JSX.Element | null {
const canCall = !(useSelector(getHasIncomingCall) || useSelector(getHasOngoingCall)); if (window?.lokiFeatureFlags.useCallMessage) {
return ( const canCall = !(useSelector(getHasIncomingCall) || useSelector(getHasOngoingCall));
<Item return (
onClick={async () => { <Item
// TODO: either pass param to callRecipient or call different call methods based on item selected. onClick={async () => {
// TODO: one time redux-persisted permission modal? // TODO: either pass param to callRecipient or call different call methods based on item selected.
const convo = getConversationController().get(conversationId); // TODO: one time redux-persisted permission modal?
const convo = getConversationController().get(conversationId);
if (!canCall) { if (!canCall) {
ToastUtils.pushUnableToCall(); ToastUtils.pushUnableToCall();
return; return;
} }
if (convo) { if (convo) {
convo.callState = 'connecting'; convo.callState = 'connecting';
await convo.commit(); await convo.commit();
await CallManager.USER_callRecipient(convo.id); await CallManager.USER_callRecipient(convo.id);
} }
}} }}
> >
{'Video Call'} {'Video Call'}
</Item> </Item>
); );
}
return null;
} }
export function getDisappearingMenuItem( export function getDisappearingMenuItem(

View File

@ -1,6 +1,5 @@
import Electron from 'electron'; import { ipcRenderer } from 'electron';
const { ipcRenderer } = Electron;
// tslint:disable: no-require-imports no-var-requires one-variable-per-declaration no-void-expression // tslint:disable: no-require-imports no-var-requires one-variable-per-declaration no-void-expression
import _ from 'lodash'; import _ from 'lodash';

View File

@ -6,13 +6,12 @@ import _ from 'lodash';
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import tls from 'tls'; import tls from 'tls';
import Electron from 'electron'; import { remote } from 'electron';
import { sha256 } from '../crypto'; import { sha256 } from '../crypto';
import * as Data from '../../../ts/data/data'; import * as Data from '../../../ts/data/data';
import pRetry from 'p-retry'; import pRetry from 'p-retry';
import { SeedNodeAPI } from '.'; import { SeedNodeAPI } from '.';
const { remote } = Electron;
// tslint:disable: function-name // tslint:disable: function-name
export type SeedNode = { export type SeedNode = {

View File

@ -465,7 +465,13 @@ export async function retrieveNextMessages(
// let exceptions bubble up // let exceptions bubble up
// no retry for this one as this a call we do every few seconds while polling for messages // no retry for this one as this a call we do every few seconds while polling for messages
const result = await snodeRpc({ method: 'retrieve', params, targetNode, associatedWith }); const result = await snodeRpc({
method: 'retrieve',
params,
targetNode,
associatedWith,
timeout: 4000,
});
if (!result) { if (!result) {
window?.log?.warn( window?.log?.warn(
@ -662,7 +668,7 @@ export const forceNetworkDeletion = async (): Promise<Array<string> | null> => {
minTimeout: exports.TEST_getMinTimeout(), minTimeout: exports.TEST_getMinTimeout(),
onFailedAttempt: e => { onFailedAttempt: e => {
window?.log?.warn( window?.log?.warn(
`delete_all OUTER request attempt #${e.attemptNumber} failed. ${e.retriesLeft} retries left...` `delete_all OUTER request attempt #${e.attemptNumber} failed. ${e.retriesLeft} retries left... ${e.message}`
); );
}, },
} }
@ -770,6 +776,12 @@ export const networkDeleteMessages = async (hashes: Array<string>): Promise<any>
snodeToMakeRequestTo.pubkey_ed25519 snodeToMakeRequestTo.pubkey_ed25519
)} due to error: ${reason}: ${statusCode}` )} due to error: ${reason}: ${statusCode}`
); );
// if we tried to make the delete on a snode not in our swarm, just trigger a pRetry error so the outer block here finds new snodes to make the request to.
if (statusCode === 421) {
throw new pRetry.AbortError(
'421 error on network delete_all. Retrying with a new snode'
);
}
} else { } else {
window?.log?.warn( window?.log?.warn(
`Could not delete data from ${ed25519Str( `Could not delete data from ${ed25519Str(

View File

@ -19,13 +19,14 @@ async function lokiFetch({
url, url,
associatedWith, associatedWith,
targetNode, targetNode,
timeout,
}: { }: {
url: string; url: string;
options: FetchOptions; options: FetchOptions;
targetNode?: Snode; targetNode?: Snode;
associatedWith?: string; associatedWith?: string;
timeout: number;
}): Promise<undefined | SnodeResponse> { }): Promise<undefined | SnodeResponse> {
const timeout = 10000;
const method = options.method || 'GET'; const method = options.method || 'GET';
const fetchOptions = { const fetchOptions = {
@ -97,11 +98,13 @@ export async function snodeRpc(
params, params,
targetNode, targetNode,
associatedWith, associatedWith,
timeout = 10000,
}: { }: {
method: string; method: string;
params: any; params: any;
targetNode: Snode; targetNode: Snode;
associatedWith?: string; associatedWith?: string;
timeout?: number;
} //the user pubkey this call is for. if the onion request fails, this is used to handle the error for this user swarm for instance } //the user pubkey this call is for. if the onion request fails, this is used to handle the error for this user swarm for instance
): Promise<undefined | SnodeResponse> { ): Promise<undefined | SnodeResponse> {
const url = `https://${targetNode.ip}:${targetNode.port}/storage_rpc/v1`; const url = `https://${targetNode.ip}:${targetNode.port}/storage_rpc/v1`;
@ -135,5 +138,6 @@ export async function snodeRpc(
options: fetchOptions, options: fetchOptions,
targetNode, targetNode,
associatedWith, associatedWith,
timeout,
}); });
} }

View File

@ -530,9 +530,8 @@ async function handle421InvalidSwarm({
// this does not make much sense to have a 421 without a publicKey set. // this does not make much sense to have a 421 without a publicKey set.
throw new Error('status 421 without a final destination or no associatedWith makes no sense'); throw new Error('status 421 without a final destination or no associatedWith makes no sense');
} }
window?.log?.info(`Invalidating swarm for ${associatedWith}`); window?.log?.info(`Invalidating swarm for ${ed25519Str(associatedWith)}`);
const exceptionMessage = '421 handled. Retry this request with a new targetNode';
try { try {
const parsedBody = JSON.parse(body); const parsedBody = JSON.parse(body);
@ -545,12 +544,12 @@ async function handle421InvalidSwarm({
); );
await updateSwarmFor(associatedWith, parsedBody.snodes); await updateSwarmFor(associatedWith, parsedBody.snodes);
throw new pRetry.AbortError(exceptionMessage); throw new pRetry.AbortError(ERROR_421_HANDLED_RETRY_REQUEST);
} }
// remove this node from the swarm of this pubkey // remove this node from the swarm of this pubkey
await dropSnodeFromSwarmIfNeeded(associatedWith, snodeEd25519); await dropSnodeFromSwarmIfNeeded(associatedWith, snodeEd25519);
} catch (e) { } catch (e) {
if (e.message !== exceptionMessage) { if (e.message !== ERROR_421_HANDLED_RETRY_REQUEST) {
window?.log?.warn( window?.log?.warn(
'Got error while parsing 421 result. Dropping this snode from the swarm of this pubkey', 'Got error while parsing 421 result. Dropping this snode from the swarm of this pubkey',
e e

View File

@ -289,7 +289,7 @@ export class SwarmPolling {
retries: 1, retries: 1,
onFailedAttempt: e => { onFailedAttempt: e => {
window?.log?.warn( window?.log?.warn(
`retrieveNextMessages attempt #${e.attemptNumber} failed. ${e.retriesLeft} retries left...` `retrieveNextMessages attempt #${e.attemptNumber} failed. ${e.retriesLeft} retries left... ${e.name}`
); );
}, },
} }

View File

@ -2,7 +2,6 @@ import { v4 as uuid } from 'uuid';
type Job<ResultType> = (() => PromiseLike<ResultType>) | (() => ResultType); type Job<ResultType> = (() => PromiseLike<ResultType>) | (() => ResultType);
// TODO: This needs to replace js/modules/job_queue.js
export class JobQueue { export class JobQueue {
private pending?: Promise<any> = Promise.resolve(); private pending?: Promise<any> = Promise.resolve();
private readonly jobs: Map<string, Promise<unknown>> = new Map(); private readonly jobs: Map<string, Promise<unknown>> = new Map();

View File

@ -1,35 +0,0 @@
import ByteBuffer from 'bytebuffer';
/**
* Converts any object to a valid ts protobuf object.
*
* This is needed because there's a very jarring difference between `protobufjs` and `protobufts`.
* `protobufjs` returns all `bytes` as `ByteBuffer` where as `protobufts` returns all `bytes` as `Uint8Array`.
*/
export function convertToTS(object: any): any {
// No idea why js `ByteBuffer` and ts `ByteBuffer` differ ...
if (object instanceof Uint8Array) {
return object;
} else if (object && object.constructor && object.constructor.name === 'ByteBuffer') {
return new Uint8Array(object.toArrayBuffer());
} else if (
object instanceof ByteBuffer ||
object instanceof Buffer ||
object instanceof ArrayBuffer ||
object instanceof SharedArrayBuffer
) {
const arrayBuffer = ByteBuffer.wrap(object).toArrayBuffer();
return new Uint8Array(arrayBuffer);
} else if (Array.isArray(object)) {
return object.map(convertToTS);
} else if (object && typeof object === 'object') {
const keys = Object.keys(object);
const values: { [key: string]: any } = {};
for (const key of keys) {
values[key] = convertToTS(object[key]);
}
return values;
}
return object;
}

View File

@ -2,7 +2,6 @@ import * as MessageUtils from './Messages';
import * as GroupUtils from './Groups'; import * as GroupUtils from './Groups';
import * as StringUtils from './String'; import * as StringUtils from './String';
import * as PromiseUtils from './Promise'; import * as PromiseUtils from './Promise';
import * as ProtobufUtils from './Protobuf';
import * as MenuUtils from '../../components/session/menu/Menu'; import * as MenuUtils from '../../components/session/menu/Menu';
import * as ToastUtils from './Toast'; import * as ToastUtils from './Toast';
import * as UserUtils from './User'; import * as UserUtils from './User';
@ -20,7 +19,6 @@ export {
GroupUtils, GroupUtils,
StringUtils, StringUtils,
PromiseUtils, PromiseUtils,
ProtobufUtils,
MenuUtils, MenuUtils,
ToastUtils, ToastUtils,
UserUtils, UserUtils,

View File

@ -1,29 +0,0 @@
// tslint:disable no-console
import { join } from 'path';
import { fromPairs, groupBy, map } from 'lodash';
import { ExceptionType } from './types';
import { loadJSON } from './util';
const exceptionsPath = join(__dirname, 'exceptions.json');
const exceptions: Array<ExceptionType> = loadJSON(exceptionsPath);
const byRule = groupBy(exceptions, 'rule');
const byRuleThenByCategory = fromPairs(
map(byRule, (list, ruleName) => {
const byCategory = groupBy(list, 'reasonCategory');
return [
ruleName,
fromPairs(
map(byCategory, (innerList, categoryName) => {
return [categoryName, innerList.length];
})
),
];
})
);
console.log(JSON.stringify(byRuleThenByCategory, null, ' '));

File diff suppressed because it is too large Load Diff

View File

@ -1,266 +0,0 @@
// tslint:disable no-console
import { readFileSync } from 'fs';
import { join, relative } from 'path';
// @ts-ignore
import * as glob from 'glob';
import { forEach, some, values } from 'lodash';
import { ExceptionType, REASONS, RuleType } from './types';
import { ENCODING, loadJSON, sortExceptions } from './util';
const ALL_REASONS = REASONS.join('|');
const now = new Date();
function getExceptionKey(exception: any) {
return `${exception.rule}-${exception.path}-${exception.lineNumber}`;
}
function createLookup(list: Array<any>) {
const lookup = Object.create(null);
forEach(list, exception => {
const key = getExceptionKey(exception);
if (lookup[key]) {
throw new Error(`Duplicate exception found for key ${key}`);
}
lookup[key] = exception;
});
return lookup;
}
const rulesPath = join(__dirname, 'rules.json');
const exceptionsPath = join(__dirname, 'exceptions.json');
const basePath = join(__dirname, '../../..');
const searchPattern = join(basePath, '**/*.{js,ts,tsx}');
const rules: Array<RuleType> = loadJSON(rulesPath);
const exceptions: Array<ExceptionType> = loadJSON(exceptionsPath);
const exceptionsLookup = createLookup(exceptions);
let scannedCount = 0;
const allSourceFiles = glob.sync(searchPattern, { nodir: true });
const results: Array<ExceptionType> = [];
const excludedFiles = [
// High-traffic files in our project
'^js/background.js',
// Generated files
'^js/components.js',
'^js/curve/',
'^js/libtextsecure.js',
'^js/util_worker.js',
'^libtextsecure/components.js',
'^libtextsecure/test/test.js',
'^test/test.js',
// From libsignal-protocol-javascript project
'^libtextsecure/libsignal-protocol.js',
// Test files
'^libtextsecure/test/*',
'^test/*',
// Modules we trust
'^node_modules/react/*',
'^node_modules/react-dom/*',
// Modules used only in test/development scenarios
'^node_modules/@types/*',
'^node_modules/ajv/*',
'^node_modules/amdefine/*',
'^node_modules/anymatch/*',
'^node_modules/app-builder-lib/*',
'^node_modules/asn1\\.js/*',
'^node_modules/autoprefixer/*',
'^node_modules/babel*',
'^node_modules/bluebird/*',
'^node_modules/body-parser/*',
'^node_modules/bower/*',
'^node_modules/buble/*',
'^node_modules/builder-util/*',
'^node_modules/builder-util-runtime/*',
'^node_modules/chai/*',
'^node_modules/cli-table2/*',
'^node_modules/codemirror/*',
'^node_modules/coffee-script/*',
'^node_modules/compression/*',
'^node_modules/degenerator/*',
'^node_modules/detect-port-alt/*',
'^node_modules/electron-builder/*',
'^node_modules/electron-osx-sign/*',
'^node_modules/electron-publish/*',
'^node_modules/escodegen/*',
'^node_modules/eslint*',
'^node_modules/esprima/*',
'^node_modules/express/*',
'^node_modules/finalhandler/*',
'^node_modules/fsevents/*',
'^node_modules/globule/*',
'^node_modules/grunt*',
'^node_modules/handle-thing/*',
'^node_modules/har-validator/*',
'^node_modules/highlight\\.js/*',
'^node_modules/hpack\\.js/*',
'^node_modules/http-proxy-middlewar/*',
'^node_modules/icss-utils/*',
'^node_modules/istanbul*',
'^node_modules/jimp/*',
'^node_modules/jquery/*',
'^node_modules/jss/*',
'^node_modules/jss-global/*',
'^node_modules/livereload-js/*',
'^node_modules/lolex/*',
'^node_modules/magic-string/*',
'^node_modules/mocha/*',
'^node_modules/minimatch/*',
'^node_modules/nise/*',
'^node_modules/node-sass-import-once/*',
'^node_modules/node-sass/*',
'^node_modules/nsp/*',
'^node_modules/phantomjs-prebuilt/*',
'^node_modules/postcss*',
'^node_modules/preserve/*',
'^node_modules/prettier/*',
'^node_modules/protobufjs/cli/*',
'^node_modules/ramda/*',
'^node_modules/react-docgen/*',
'^node_modules/react-error-overlay/*',
'^node_modules/recast/*',
'^node_modules/reduce-css-calc/*',
'^node_modules/resolve/*',
'^node_modules/sass-graph/*',
'^node_modules/scss-tokenizer/*',
'^node_modules/send/*',
'^node_modules/serve-index/*',
'^node_modules/sinon/*',
'^node_modules/snapdragon-util/*',
'^node_modules/snapdragon/*',
'^node_modules/sockjs-client/*',
'^node_modules/style-loader/*',
'^node_modules/svgo/*',
'^node_modules/text-encoding/*',
'^node_modules/tinycolor2/*',
'^node_modules/to-ast/*',
'^node_modules/trough/*',
'^node_modules/ts-loader/*',
'^node_modules/tslint*',
'^node_modules/tweetnacl/*',
'^node_modules/typescript/*',
'^node_modules/uglify-es/*',
'^node_modules/uglify-js/*',
'^node_modules/use/*',
'^node_modules/vary/*',
'^node_modules/vm-browserify/*',
'^node_modules/webdriverio/*',
'^node_modules/webpack*',
'^node_modules/xmldom/*',
'^node_modules/xml-parse-from-string/*',
];
function setupRules(allRules: Array<RuleType>) {
forEach(allRules, (rule, index) => {
if (!rule.name) {
throw new Error(`Rule at index ${index} is missing a name`);
}
if (!rule.expression) {
throw new Error(`Rule '${rule.name}' is missing an expression`);
}
rule.regex = new RegExp(rule.expression, 'g');
});
}
setupRules(rules);
forEach(allSourceFiles, file => {
const relativePath = relative(basePath, file).replace(/\\/g, '/');
if (
some(excludedFiles, excluded => {
const regex = new RegExp(excluded);
return regex.test(relativePath);
})
) {
return;
}
scannedCount += 1;
const fileContents = readFileSync(file, ENCODING);
const lines = fileContents.split('\n');
forEach(rules, (rule: RuleType) => {
const excludedModules = rule.excludedModules || [];
if (some(excludedModules, module => relativePath.startsWith(module))) {
return;
}
forEach(lines, (rawLine, lineIndex) => {
const line = rawLine.replace(/\r/g, '');
if (!rule.regex.test(line)) {
return;
}
const path = relativePath;
const lineNumber = lineIndex + 1;
const exceptionKey = getExceptionKey({
rule: rule.name,
path: relativePath,
lineNumber,
});
const exception = exceptionsLookup[exceptionKey];
if (exception && (!exception.line || exception.line === line)) {
// tslint:disable-next-line no-dynamic-delete
delete exceptionsLookup[exceptionKey];
return;
}
results.push({
rule: rule.name,
path,
line: line.length < 300 ? line : undefined,
lineNumber,
reasonCategory: ALL_REASONS,
updated: now.toJSON(),
reasonDetail: '<optional>',
});
});
});
});
const unusedExceptions = values(exceptionsLookup);
console.log(
`${scannedCount} files scanned.`,
`${results.length} questionable lines,`,
`${unusedExceptions.length} unused exceptions,`,
`${exceptions.length} total exceptions.`
);
if (results.length === 0 && unusedExceptions.length === 0) {
process.exit();
}
console.log();
console.log('Questionable lines:');
console.log(JSON.stringify(sortExceptions(results), null, ' '));
if (unusedExceptions.length) {
console.log();
console.log('Unused exceptions!');
console.log(JSON.stringify(sortExceptions(unusedExceptions), null, ' '));
}
process.exit(1);

View File

@ -1,135 +0,0 @@
[
{
"name": "eval",
"expression": "\\beval\\(",
"reason": "Arbitrary code execution"
},
{
"name": "DOM-innerHTML",
"expression": "\\binnerHTML\\b",
"reason": "Potential XSS"
},
{
"name": "DOM-outerHTML",
"expression": "\\bouterHTML\\b",
"reason": "Potential XSS"
},
{
"name": "DOM-document.write(",
"expression": "\\bdocument.write(ln)?\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-$(",
"expression": "\\$\\(",
"reason": "Potential XSS",
"excludedModules": ["node_modules/prelude-ls"]
},
{
"name": "jQuery-html(",
"expression": "\\bhtml\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-append(",
"expression": "\\bappend\\(",
"reason": "Potential XSS",
"excludedModules": [
"components/bytebuffer",
"components/protobuf",
"node_modules/google-libphonenumber",
"node_modules/handlebars"
]
},
{
"name": "jQuery-appendTo(",
"expression": "\\bappendTo\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-insertAfter(",
"expression": "\\binsertAfter\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-insertBefore(",
"expression": "\\binsertBefore\\(",
"reason": "Potential XSS",
"excludedModules": ["node_modules/react-dom"]
},
{
"name": "jQuery-prepend(",
"expression": "\\bprepend\\(",
"reason": "Potential XSS",
"excludedModules": ["components/bytebuffer", "node_modules/handlebars"]
},
{
"name": "jQuery-prependTo(",
"expression": "\\bprependTo\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-wrap(",
"expression": "\\bwrap\\(",
"reason": "Potential XSS",
"excludedModules": [
"components/bytebuffer",
"components/protobuf",
"node_modules/handlebars",
"node_modules/lodash"
]
},
{
"name": "jQuery-wrapInner(",
"expression": "\\bwrapInner\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-wrapAll(",
"expression": "\\bwrapAll\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-before(",
"expression": "\\bbefore\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-after(",
"expression": "\\bafter\\(",
"reason": "Potential XSS"
},
{
"name": "jQuery-globalEval(",
"expression": "\\bglobalEval\\(",
"reason": "Arbitrary code execution"
},
{
"name": "jQuery-getScript(",
"expression": "\\bgetScript\\(",
"reason": "Arbitrary code execution"
},
{
"name": "jQuery-load(",
"expression": "\\bload\\(",
"reason": "Arbitrary code execution"
},
{
"name": "fbjs-createNodesFromMarkup",
"expression": "\\bcreateNodesFromMarkup\\b",
"reason": "Potential XSS, pipes input to innerHTML",
"excludedModules": ["node_modules/react-dom", "node_modules/fbjs"]
},
{
"name": "thenify-multiArgs",
"expression": "\\bmultiArgs\\b",
"reason": "Potential arbitrary code execution, piped to eval",
"excludedModules": ["node_modules/thenify"]
},
{
"name": "bluebird-toFastProperties",
"expression": "\\btoFastProperties\\b",
"reason": "Whatever is provided is sent straight to eval()",
"excludedModules": []
}
]

View File

@ -1,14 +0,0 @@
// tslint:disable no-console
import { join } from 'path';
import { writeFileSync } from 'fs';
import { ExceptionType } from './types';
import { loadJSON, sortExceptions } from './util';
const exceptionsPath = join(__dirname, 'exceptions.json');
const exceptions: Array<ExceptionType> = loadJSON(exceptionsPath);
const sorted = sortExceptions(exceptions);
writeFileSync(exceptionsPath, JSON.stringify(sorted, null, ' '));

View File

@ -1,63 +0,0 @@
// Tool requirements:
// - Feed it a set of regular expressions with descriptions as to what the risks are
// - Feed it also a set of exceptions
// - It would tell us if there were any new matches that didn't already have exceptions
//
// Rules:
// {
// "name": "rule-name",
// "expression": "^regex-as-string$",
// "reason": "Reason that this expression is dangerous"
// }
//
// Categories of reasons - low to high risk:
// "falseMatch"
// "testCode"
// "exampleCode"
// "otherUtilityCode"
// "regexMatchedSafeCode"
// "notExercisedByOurApp"
// "ruleNeeded"
// "usageTrusted"
//
// Exceptions:
// [{
// "rule": "rule-name",
// "path": "path/to/filename.js",
// "lineNumber": 45,
// "reasonCategory": "<category from list above>",
// "updated": "2018-09-08T00:21:13.180Z",
// "reasonDetail": "<Optional additional information about why this is okay>"
// }]
//
// When the tool finds issues it outputs them in exception format to make it easy to add
// to the exceptions.json file
export const REASONS = [
'falseMatch',
'testCode',
'exampleCode',
'otherUtilityCode',
'regexMatchedSafeCode',
'notExercisedByOurApp',
'ruleNeeded',
'usageTrusted',
];
export type RuleType = {
name: string;
expression?: string;
reason: string;
regex: RegExp;
excludedModules?: Array<string>;
};
export type ExceptionType = {
rule: string;
path: string;
line?: string;
lineNumber: number;
reasonCategory: string;
updated: string;
reasonDetail: string;
};

View File

@ -1,24 +0,0 @@
// tslint:disable no-console
import { readFileSync } from 'fs';
import { orderBy } from 'lodash';
import { ExceptionType } from './types';
export const ENCODING = 'utf8';
export function loadJSON(target: string) {
try {
const contents = readFileSync(target, ENCODING);
return JSON.parse(contents);
} catch (error) {
console.log(`Error loading JSON from ${target}: ${error.stack}`);
throw error;
}
}
export function sortExceptions(exceptions: Array<ExceptionType>) {
return orderBy(exceptions, ['path', 'lineNumber', 'rule']);
}

2
ts/window.d.ts vendored
View File

@ -16,8 +16,6 @@ We declare window stuff here instead of global.d.ts because we are importing oth
If you import anything in global.d.ts, the type system won't work correctly. If you import anything in global.d.ts, the type system won't work correctly.
*/ */
type UtilWorkerFunctionType = (fnName: string, ...args: any) => Promise<any>;
declare global { declare global {
interface Window { interface Window {
CONSTANTS: any; CONSTANTS: any;

View File

@ -1786,11 +1786,6 @@ boolean@^3.0.0:
resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.1.tgz#35ecf2b4a2ee191b0b44986f14eb5f052a5cbb4f" resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.1.tgz#35ecf2b4a2ee191b0b44986f14eb5f052a5cbb4f"
integrity sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA== integrity sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==
bower@1.8.2:
version "1.8.2"
resolved "https://registry.yarnpkg.com/bower/-/bower-1.8.2.tgz#adf53529c8d4af02ef24fb8d5341c1419d33e2f7"
integrity sha1-rfU1KcjUrwLvJPuNU0HBQZ0z4vc=
boxen@^4.2.0: boxen@^4.2.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64"