more cleanup
This commit is contained in:
parent
d02d77a212
commit
1e8c8991ad
12
.aptly.conf
12
.aptly.conf
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,8 +32,7 @@ js/util_worker.js
|
|||
libtextsecure/test/blanket_mocha.js
|
||||
mnemonic_languages/**
|
||||
|
||||
# Managed by package manager (`bower` and `yarn`/`npm`):
|
||||
/bower.json
|
||||
# Managed by package manager (`yarn`/`npm`):
|
||||
/package.json
|
||||
|
||||
# Symlink into third-party `components`:
|
||||
|
|
30
Gruntfile.js
30
Gruntfile.js
|
@ -5,18 +5,32 @@ const sass = require('node-sass');
|
|||
|
||||
/* 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 => {
|
||||
const bower = grunt.file.readJSON('bower.json');
|
||||
const components = [];
|
||||
// eslint-disable-next-line guard-for-in, no-restricted-syntax
|
||||
for (const i in bower.concat.app) {
|
||||
components.push(bower.concat.app[i]);
|
||||
for (const i in toConcatForApp) {
|
||||
components.push(toConcatForApp[i]);
|
||||
}
|
||||
|
||||
const libtextsecurecomponents = [];
|
||||
// eslint-disable-next-line guard-for-in, no-restricted-syntax
|
||||
for (const i in bower.concat.libtextsecure) {
|
||||
libtextsecurecomponents.push(bower.concat.libtextsecure[i]);
|
||||
for (const i in toConcatForComponentTextsecure) {
|
||||
libtextsecurecomponents.push(toConcatForComponentTextsecure[i]);
|
||||
}
|
||||
|
||||
const utilWorkerComponents = [
|
||||
|
@ -58,13 +72,7 @@ module.exports = grunt => {
|
|||
'libtextsecure/crypto.js',
|
||||
'libtextsecure/storage.js',
|
||||
'libtextsecure/storage/user.js',
|
||||
'libtextsecure/storage/groups.js',
|
||||
'libtextsecure/protobufs.js',
|
||||
'libtextsecure/helpers.js',
|
||||
'libtextsecure/stringview.js',
|
||||
'libtextsecure/event_target.js',
|
||||
'libtextsecure/http-resources.js',
|
||||
'libtextsecure/message_receiver.js',
|
||||
],
|
||||
dest: 'js/libtextsecure.js',
|
||||
},
|
||||
|
|
|
@ -453,5 +453,6 @@
|
|||
"unableToCall": "cancel your ongoing call first",
|
||||
"unableToCallTitle": "Cannot start new call",
|
||||
"callMissed": "Missed call from $name$",
|
||||
"callMissedTitle": "Call missed"
|
||||
"callMissedTitle": "Call missed",
|
||||
"startVideoCall": "Start Video Call"
|
||||
}
|
||||
|
|
|
@ -32,9 +32,7 @@
|
|||
-->
|
||||
|
||||
<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/legacy_storage.js"></script>
|
||||
<script type="text/javascript" src="js/libtextsecure.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/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/views/react_wrapper_view.js"></script>
|
||||
|
|
|
@ -32,9 +32,7 @@
|
|||
-->
|
||||
|
||||
<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/legacy_storage.js"></script>
|
||||
<script type="text/javascript" src="js/libtextsecure.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/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/views/react_wrapper_view.js"></script>
|
||||
|
|
41
bower.json
41
bower.json
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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 cursor’s `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
44
index.html
44
index.html
|
@ -1,23 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html class='no-js' lang='en'>
|
||||
<head>
|
||||
<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 http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Session</title>
|
||||
<meta name="description" content="">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href='/images/session/session_icon_128.png' rel='shortcut icon'>
|
||||
<link href="/stylesheets/manifest.css" rel="stylesheet" type="text/css" />
|
||||
<script type="text/javascript" src="js/chromium.js"></script>
|
||||
</head>
|
||||
<body id="signal-container" class='signal index'>
|
||||
<div class='app-loading-screen'>
|
||||
<div class="content session-full-logo">
|
||||
<img src="images/session/brand.svg" class="session-brand-logo" />
|
||||
<img src="images/session/session-text.svg" class="session-text-logo" />
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
</body>
|
||||
<html class="no-js" lang="en">
|
||||
<head>
|
||||
<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 http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>Session</title>
|
||||
<meta name="description" content="" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link href="/images/session/session_icon_128.png" rel="shortcut icon" />
|
||||
<link href="/stylesheets/manifest.css" rel="stylesheet" type="text/css" />
|
||||
</head>
|
||||
<body id="signal-container" class="signal index">
|
||||
<div class="app-loading-screen">
|
||||
<div class="content session-full-logo">
|
||||
<img src="images/session/brand.svg" class="session-brand-logo" />
|
||||
<img src="images/session/session-text.svg" class="session-text-logo" />
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -66,11 +66,6 @@
|
|||
window.getGlobalOnlineStatus = () => window.globalOnlineStatus;
|
||||
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('environment:', window.getEnvironment());
|
||||
const restartReason = localStorage.getItem('restart-reason');
|
||||
|
@ -149,15 +144,6 @@
|
|||
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 () => {
|
||||
// Stop background processing
|
||||
window.libsession.Utils.AttachmentDownloads.stop();
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
};
|
||||
})();
|
|
@ -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 = {
|
||||
isWindows: false,
|
||||
|
@ -38010,111 +37702,6 @@ var FS = {
|
|||
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 = {
|
||||
splitPath: function(filename) {
|
||||
|
|
122
js/database.js
122
js/database.js
|
@ -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;
|
||||
});
|
||||
})();
|
|
@ -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();
|
||||
},
|
||||
};
|
||||
})();
|
|
@ -3,13 +3,13 @@
|
|||
/* eslint strict: ['error', 'never'] */
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const electron = require('electron');
|
||||
const { ipcRenderer } = require('electron');
|
||||
const _ = require('lodash');
|
||||
|
||||
const debuglogs = require('./modules/debuglogs');
|
||||
const Privacy = require('./modules/privacy');
|
||||
|
||||
const ipc = electron.ipcRenderer;
|
||||
const ipc = ipcRenderer;
|
||||
|
||||
// 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
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
};
|
|
@ -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,
|
||||
};
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
const Crypto = require('./crypto');
|
||||
const Data = require('../../ts/data/data');
|
||||
const Database = require('./database');
|
||||
const Emoji = require('../../ts/util/emoji');
|
||||
const Notifications = require('../../ts/notifications');
|
||||
const OS = require('../../ts/OS');
|
||||
|
@ -143,7 +142,6 @@ exports.setup = (options = {}) => {
|
|||
Components,
|
||||
Crypto,
|
||||
Data,
|
||||
Database,
|
||||
Emoji,
|
||||
LinkPreviews,
|
||||
Migrations,
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
/* global setTimeout */
|
||||
|
||||
exports.sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
|
@ -48,7 +48,6 @@
|
|||
}
|
||||
},
|
||||
openStandalone() {
|
||||
window.addSetupMenuItems();
|
||||
this.resetViews();
|
||||
this.standaloneView = new Whisper.SessionRegistrationView();
|
||||
this.openView(this.standaloneView);
|
||||
|
|
|
@ -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;
|
||||
})();
|
|
@ -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 = {
|
||||
isWindows: false, staticInit: function () {
|
||||
NODEFS.isWindows = !!process.platform.match(/^win/);
|
||||
|
@ -4049,79 +3792,7 @@ var libsignal
|
|||
} else {
|
||||
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 = {
|
||||
splitPath: function (filename) {
|
||||
var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
|
||||
|
|
|
@ -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');
|
||||
})();
|
|
@ -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
23
main.js
|
@ -597,12 +597,10 @@ async function showDebugLogWindow() {
|
|||
debugLogWindow.loadURL(prepareURL([__dirname, 'debug_log.html'], { theme }));
|
||||
|
||||
debugLogWindow.on('closed', () => {
|
||||
removeDarkOverlay();
|
||||
debugLogWindow = null;
|
||||
});
|
||||
|
||||
debugLogWindow.once('ready-to-show', () => {
|
||||
addDarkOverlay();
|
||||
debugLogWindow.show();
|
||||
});
|
||||
}
|
||||
|
@ -820,14 +818,6 @@ ipc.on('locale-data', event => {
|
|||
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', () => {
|
||||
setupMenu({
|
||||
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
|
||||
ipc.on('get-media-permissions', event => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
|
|
|
@ -175,7 +175,6 @@
|
|||
"@types/webpack": "^5.28.0",
|
||||
"arraybuffer-loader": "1.0.3",
|
||||
"asar": "0.14.0",
|
||||
"bower": "1.8.2",
|
||||
"chai": "4.3.4",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-bytes": "^0.1.2",
|
||||
|
|
33
preload.js
33
preload.js
|
@ -2,18 +2,12 @@
|
|||
/* global Whisper: false */
|
||||
/* global window: false */
|
||||
const path = require('path');
|
||||
const electron = require('electron');
|
||||
|
||||
const { webFrame } = electron;
|
||||
const { webFrame, remote, clipboard, ipcRenderer } = require('electron');
|
||||
const semver = require('semver');
|
||||
|
||||
const { deferredToPromise } = require('./js/modules/deferred_to_promise');
|
||||
const { JobQueue } = require('./js/modules/job_queue');
|
||||
|
||||
const { app } = electron.remote;
|
||||
const { clipboard } = electron;
|
||||
|
||||
window.PROTO_ROOT = 'protos';
|
||||
const { app } = remote;
|
||||
|
||||
const config = require('url').parse(window.location.toString(), true).query;
|
||||
|
||||
|
@ -43,7 +37,6 @@ window.getCommitHash = () => config.commitHash;
|
|||
window.getNodeVersion = () => config.node_version;
|
||||
window.getHostName = () => config.hostname;
|
||||
window.getServerTrustRoot = () => config.serverTrustRoot;
|
||||
window.JobQueue = JobQueue;
|
||||
window.isBehindProxy = () => Boolean(config.proxyUrl);
|
||||
|
||||
window.lokiFeatureFlags = {
|
||||
|
@ -83,7 +76,7 @@ window.versionInfo = {
|
|||
|
||||
window.wrapDeferred = deferredToPromise;
|
||||
|
||||
const ipc = electron.ipcRenderer;
|
||||
const ipc = ipcRenderer;
|
||||
const localeMessages = ipc.sendSync('locale-data');
|
||||
|
||||
window.updateZoomFactor = () => {
|
||||
|
@ -99,8 +92,6 @@ window.getZoomFactor = () => {
|
|||
webFrame.getZoomFactor();
|
||||
};
|
||||
|
||||
window.setBadgeCount = count => ipc.send('set-badge-count', count);
|
||||
|
||||
// Set the password for the database
|
||||
window.setPassword = (passPhrase, oldPhrase) =>
|
||||
new Promise((resolve, reject) => {
|
||||
|
@ -170,21 +161,6 @@ ipc.on('get-theme-setting', () => {
|
|||
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) => {
|
||||
// Comparison value allows you to pull boolean values from any type.
|
||||
// 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
|
||||
|
||||
require('./js/logging');
|
||||
|
|
|
@ -37,17 +37,6 @@ audio {
|
|||
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:after {
|
||||
display: table;
|
||||
|
|
|
@ -33,11 +33,6 @@
|
|||
}
|
||||
|
||||
// _global
|
||||
|
||||
.dark-overlay {
|
||||
background-color: $color-gray-95;
|
||||
}
|
||||
|
||||
.title-bar {
|
||||
color: $color-dark-05;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
import Electron from 'electron';
|
||||
const { shell } = Electron;
|
||||
import { shell } from 'electron';
|
||||
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
|
|
|
@ -323,29 +323,33 @@ export function getMarkAllReadMenuItem(conversationId: string): JSX.Element | nu
|
|||
}
|
||||
|
||||
export function getStartCallMenuItem(conversationId: string): JSX.Element | null {
|
||||
const canCall = !(useSelector(getHasIncomingCall) || useSelector(getHasOngoingCall));
|
||||
return (
|
||||
<Item
|
||||
onClick={async () => {
|
||||
// TODO: either pass param to callRecipient or call different call methods based on item selected.
|
||||
// TODO: one time redux-persisted permission modal?
|
||||
const convo = getConversationController().get(conversationId);
|
||||
if (window?.lokiFeatureFlags.useCallMessage) {
|
||||
const canCall = !(useSelector(getHasIncomingCall) || useSelector(getHasOngoingCall));
|
||||
return (
|
||||
<Item
|
||||
onClick={async () => {
|
||||
// TODO: either pass param to callRecipient or call different call methods based on item selected.
|
||||
// TODO: one time redux-persisted permission modal?
|
||||
const convo = getConversationController().get(conversationId);
|
||||
|
||||
if (!canCall) {
|
||||
ToastUtils.pushUnableToCall();
|
||||
return;
|
||||
}
|
||||
if (!canCall) {
|
||||
ToastUtils.pushUnableToCall();
|
||||
return;
|
||||
}
|
||||
|
||||
if (convo) {
|
||||
convo.callState = 'connecting';
|
||||
await convo.commit();
|
||||
await CallManager.USER_callRecipient(convo.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{'Video Call'}
|
||||
</Item>
|
||||
);
|
||||
if (convo) {
|
||||
convo.callState = 'connecting';
|
||||
await convo.commit();
|
||||
await CallManager.USER_callRecipient(convo.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{'Video Call'}
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getDisappearingMenuItem(
|
||||
|
|
|
@ -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
|
||||
|
||||
import _ from 'lodash';
|
||||
|
|
|
@ -6,13 +6,12 @@ import _ from 'lodash';
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import tls from 'tls';
|
||||
import Electron from 'electron';
|
||||
import { remote } from 'electron';
|
||||
import { sha256 } from '../crypto';
|
||||
import * as Data from '../../../ts/data/data';
|
||||
import pRetry from 'p-retry';
|
||||
import { SeedNodeAPI } from '.';
|
||||
|
||||
const { remote } = Electron;
|
||||
// tslint:disable: function-name
|
||||
|
||||
export type SeedNode = {
|
||||
|
|
|
@ -465,7 +465,13 @@ export async function retrieveNextMessages(
|
|||
|
||||
// let exceptions bubble up
|
||||
// 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) {
|
||||
window?.log?.warn(
|
||||
|
@ -662,7 +668,7 @@ export const forceNetworkDeletion = async (): Promise<Array<string> | null> => {
|
|||
minTimeout: exports.TEST_getMinTimeout(),
|
||||
onFailedAttempt: e => {
|
||||
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
|
||||
)} 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 {
|
||||
window?.log?.warn(
|
||||
`Could not delete data from ${ed25519Str(
|
||||
|
|
|
@ -19,13 +19,14 @@ async function lokiFetch({
|
|||
url,
|
||||
associatedWith,
|
||||
targetNode,
|
||||
timeout,
|
||||
}: {
|
||||
url: string;
|
||||
options: FetchOptions;
|
||||
targetNode?: Snode;
|
||||
associatedWith?: string;
|
||||
timeout: number;
|
||||
}): Promise<undefined | SnodeResponse> {
|
||||
const timeout = 10000;
|
||||
const method = options.method || 'GET';
|
||||
|
||||
const fetchOptions = {
|
||||
|
@ -97,11 +98,13 @@ export async function snodeRpc(
|
|||
params,
|
||||
targetNode,
|
||||
associatedWith,
|
||||
timeout = 10000,
|
||||
}: {
|
||||
method: string;
|
||||
params: any;
|
||||
targetNode: Snode;
|
||||
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
|
||||
): Promise<undefined | SnodeResponse> {
|
||||
const url = `https://${targetNode.ip}:${targetNode.port}/storage_rpc/v1`;
|
||||
|
@ -135,5 +138,6 @@ export async function snodeRpc(
|
|||
options: fetchOptions,
|
||||
targetNode,
|
||||
associatedWith,
|
||||
timeout,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -530,9 +530,8 @@ async function handle421InvalidSwarm({
|
|||
// 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');
|
||||
}
|
||||
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 {
|
||||
const parsedBody = JSON.parse(body);
|
||||
|
||||
|
@ -545,12 +544,12 @@ async function handle421InvalidSwarm({
|
|||
);
|
||||
|
||||
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
|
||||
await dropSnodeFromSwarmIfNeeded(associatedWith, snodeEd25519);
|
||||
} catch (e) {
|
||||
if (e.message !== exceptionMessage) {
|
||||
if (e.message !== ERROR_421_HANDLED_RETRY_REQUEST) {
|
||||
window?.log?.warn(
|
||||
'Got error while parsing 421 result. Dropping this snode from the swarm of this pubkey',
|
||||
e
|
||||
|
|
|
@ -289,7 +289,7 @@ export class SwarmPolling {
|
|||
retries: 1,
|
||||
onFailedAttempt: e => {
|
||||
window?.log?.warn(
|
||||
`retrieveNextMessages attempt #${e.attemptNumber} failed. ${e.retriesLeft} retries left...`
|
||||
`retrieveNextMessages attempt #${e.attemptNumber} failed. ${e.retriesLeft} retries left... ${e.name}`
|
||||
);
|
||||
},
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ import { v4 as uuid } from 'uuid';
|
|||
|
||||
type Job<ResultType> = (() => PromiseLike<ResultType>) | (() => ResultType);
|
||||
|
||||
// TODO: This needs to replace js/modules/job_queue.js
|
||||
export class JobQueue {
|
||||
private pending?: Promise<any> = Promise.resolve();
|
||||
private readonly jobs: Map<string, Promise<unknown>> = new Map();
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -2,7 +2,6 @@ import * as MessageUtils from './Messages';
|
|||
import * as GroupUtils from './Groups';
|
||||
import * as StringUtils from './String';
|
||||
import * as PromiseUtils from './Promise';
|
||||
import * as ProtobufUtils from './Protobuf';
|
||||
import * as MenuUtils from '../../components/session/menu/Menu';
|
||||
import * as ToastUtils from './Toast';
|
||||
import * as UserUtils from './User';
|
||||
|
@ -20,7 +19,6 @@ export {
|
|||
GroupUtils,
|
||||
StringUtils,
|
||||
PromiseUtils,
|
||||
ProtobufUtils,
|
||||
MenuUtils,
|
||||
ToastUtils,
|
||||
UserUtils,
|
||||
|
|
|
@ -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
|
@ -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);
|
|
@ -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": []
|
||||
}
|
||||
]
|
|
@ -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, ' '));
|
|
@ -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;
|
||||
};
|
|
@ -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']);
|
||||
}
|
|
@ -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.
|
||||
*/
|
||||
|
||||
type UtilWorkerFunctionType = (fnName: string, ...args: any) => Promise<any>;
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
CONSTANTS: any;
|
||||
|
|
|
@ -1786,11 +1786,6 @@ boolean@^3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.1.tgz#35ecf2b4a2ee191b0b44986f14eb5f052a5cbb4f"
|
||||
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:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64"
|
||||
|
|
Loading…
Reference in New Issue