diff --git a/customize.dist/login.js b/customize.dist/login.js
index 0e932c4f6..035a11d72 100644
--- a/customize.dist/login.js
+++ b/customize.dist/login.js
@@ -22,6 +22,7 @@ define([
Feedback, LocalStore, Messages, nThen, Block, Hash) {
var Exports = {
Cred: Cred,
+ Block: Block,
// this is depended on by non-customizable files
// be careful when modifying login.js
requiredBytes: 192,
@@ -92,7 +93,7 @@ define([
};
- var loginOptionsFromBlock = function (blockInfo) {
+ var loginOptionsFromBlock = Exports.loginOptionsFromBlock = function (blockInfo) {
var opt = {};
var parsed = Hash.getSecrets('pad', blockInfo.User_hash);
opt.channelHex = parsed.channel;
@@ -102,7 +103,7 @@ define([
return opt;
};
- var loadUserObject = function (opt, cb) {
+ var loadUserObject = Exports.loadUserObject = function (opt, cb) {
var config = {
websocketURL: NetConfig.getWebsocketURL(),
channel: opt.channelHex,
@@ -527,8 +528,6 @@ define([
if (!proxy[Constants.displayNameKey]) {
proxy[Constants.displayNameKey] = uname;
}
- LocalStore.eraseTempSessionValues();
-
if (result.blockHash) {
LocalStore.setBlockHash(result.blockHash);
diff --git a/customize.dist/src/less2/pages/page-checkup.less b/customize.dist/src/less2/pages/page-checkup.less
index a4ece61a4..2a05600c7 100644
--- a/customize.dist/src/less2/pages/page-checkup.less
+++ b/customize.dist/src/less2/pages/page-checkup.less
@@ -20,6 +20,12 @@ html, body {
padding-top: 15px;
}
+ .pending {
+ border: 1px solid white;
+ .fa {
+ margin-right: 20px;
+ }
+ }
.success {
border: 1px solid green;
}
@@ -53,5 +59,9 @@ html, body {
background-color: @cp_alerts-danger-bg;
color: @cp_alerts-danger-text;
}
+
+ iframe {
+ display: none;
+ }
}
diff --git a/www/assert/assertions.js b/www/assert/assertions.js
index 8ea0a2638..a1f6944fb 100644
--- a/www/assert/assertions.js
+++ b/www/assert/assertions.js
@@ -21,8 +21,10 @@ define([], function () {
});
};
- assert.run = function (cb) {
+ assert.run = function (cb, progress) {
+ progress = progress || function () {};
var count = ASSERTS.length;
+ var total = ASSERTS.length;
var done = function (err) {
count--;
if (err) { failMessages.push(err); }
@@ -38,6 +40,7 @@ define([], function () {
ASSERTS.forEach(function (f, index) {
f(function (err) {
//console.log("test " + index);
+ progress(index, total);
done(err, index);
}, index);
});
diff --git a/www/checkup/index.html b/www/checkup/index.html
index afa469f7f..04a9502d3 100644
--- a/www/checkup/index.html
+++ b/www/checkup/index.html
@@ -6,4 +6,6 @@
+
+
diff --git a/www/checkup/inner.html b/www/checkup/inner.html
new file mode 100644
index 000000000..4554943d5
--- /dev/null
+++ b/www/checkup/inner.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/www/checkup/inner.js b/www/checkup/inner.js
new file mode 100644
index 000000000..f752bc2ad
--- /dev/null
+++ b/www/checkup/inner.js
@@ -0,0 +1,4 @@
+define([
+], function () {
+ console.log('inner loaded');
+});
diff --git a/www/checkup/main.js b/www/checkup/main.js
index 171699a22..06b69e12b 100644
--- a/www/checkup/main.js
+++ b/www/checkup/main.js
@@ -4,12 +4,21 @@ define([
'/assert/assertions.js',
'/common/hyperscript.js',
'/customize/messages.js',
+ '/common/dom-ready.js',
+ '/bower_components/nthen/index.js',
'/common/sframe-common-outer.js',
-
+ '/customize/login.js',
+ '/common/common-hash.js',
+ '/common/common-util.js',
+ '/common/pinpad.js',
+ '/common/outer/network-config.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
+ 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/customize/src/less2/pages/page-checkup.less',
-], function ($, ApiConfig, Assertions, h, Messages /*, SFCommonO*/) {
+], function ($, ApiConfig, Assertions, h, Messages, DomReady,
+ nThen, SFCommonO, Login, Hash, Util, Pinpad,
+ NetConfig) {
var assert = Assertions();
var trimSlashes = function (s) {
@@ -41,7 +50,7 @@ define([
var checkAvailability = function (url, cb) {
$.ajax({
url: url,
- date: {},
+ data: {},
complete: function (xhr) {
cb(xhr.status === 200);
},
@@ -52,10 +61,141 @@ define([
checkAvailability(trimmedUnsafe, cb);
}, _alert("Main domain is not available"));
+ // Try loading an iframe on the safe domain
+ assert(function (cb) {
+ var to;
+ nThen(function (waitFor) {
+ DomReady.onReady(waitFor());
+ }).nThen(function (waitFor) {
+ to = setTimeout(function () {
+ console.error('TIMEOUT loading iframe on the safe domain');
+ cb(false);
+ }, 5000);
+ SFCommonO.initIframe(waitFor);
+ }).nThen(function () {
+ // Iframe is loaded
+ clearTimeout(to);
+ cb(true);
+ });
+ }, _alert("Sandbox domain is not available"));
+
+ // Test Websocket
+ var evWSError = Util.mkEvent(true);
assert(function (cb) {
- console.log(trimmedSafe);
- checkAvailability(trimmedSafe, cb);
- }, _alert("Sandbox domain is not available")); // FIXME Blocked by CSP. try loading it via sframe ?
+ var ws = new WebSocket(NetConfig.getWebsocketURL());
+ var to = setTimeout(function () {
+ console.error('Websocket TIMEOUT');
+ evWSError.fire();
+ cb('TIMEOUT (5 seconds)');
+ }, 5000);
+ ws.onopen = function () {
+ clearTimeout(to);
+ cb(true);
+ };
+ ws.onerror = function (err) {
+ clearTimeout(to);
+ console.error('Websocket error', err);
+ evWSError.fire();
+ cb('WebSocket error: check your console');
+ };
+ }, _alert("Websocket is not available"));
+
+ // Test login block
+ assert(function (cb) {
+ var bytes = new Uint8Array(Login.requiredBytes);
+
+ var opt = Login.allocateBytes(bytes);
+
+ var blockUrl = Login.Block.getBlockUrl(opt.blockKeys);
+ var blockRequest = Login.Block.serialize("{}", opt.blockKeys);
+ var removeRequest = Login.Block.remove(opt.blockKeys);
+ console.log('Test block URL:', blockUrl);
+
+ var userHash = '/2/drive/edit/000000000000000000000000';
+ var secret = Hash.getSecrets('drive', userHash);
+ opt.keys = secret.keys;
+ opt.channelHex = secret.channel;
+
+ var RT, rpc, exists;
+
+ nThen(function (waitFor) {
+ Util.fetch(blockUrl, waitFor(function (err) {
+ if (err) { return; } // No block found
+ exists = true;
+ }));
+ }).nThen(function (waitFor) {
+ // If WebSockets aren't working, don't wait forever here
+ evWSError.reg(function () {
+ waitFor.abort();
+ cb("No WebSocket (test number 6)");
+ });
+ // Create proxy
+ Login.loadUserObject(opt, waitFor(function (err, rt) {
+ if (err) {
+ waitFor.abort();
+ console.error("Can't create new channel. This may also be a websocket issue.");
+ return void cb(false);
+ }
+ RT = rt;
+ var proxy = rt.proxy;
+ proxy.edPublic = opt.edPublic;
+ proxy.edPrivate = opt.edPrivate;
+ proxy.curvePublic = opt.curvePublic;
+ proxy.curvePrivate = opt.curvePrivate;
+ rt.realtime.onSettle(waitFor());
+ }));
+ }).nThen(function (waitFor) {
+ // Init RPC
+ Pinpad.create(RT.network, RT.proxy, waitFor(function (e, _rpc) {
+ if (e) {
+ waitFor.abort();
+ console.error("Can't initialize RPC", e); // INVALID_KEYS
+ return void cb(false);
+ }
+ rpc = _rpc;
+ }));
+ }).nThen(function (waitFor) {
+ // Write block
+ if (exists) { return; }
+ rpc.writeLoginBlock(blockRequest, waitFor(function (e) {
+ if (e) {
+ waitFor.abort();
+ console.error("Can't write login block", e);
+ return void cb(false);
+ }
+ }));
+ }).nThen(function (waitFor) {
+ // Read block
+ Util.fetch(blockUrl, waitFor(function (e) {
+ if (e) {
+ waitFor.abort();
+ console.error("Can't read login block", e);
+ return void cb(false);
+ }
+ }));
+ }).nThen(function (waitFor) {
+ // Remove block
+ rpc.removeLoginBlock(removeRequest, waitFor(function (e) {
+ if (e) {
+ waitFor.abort();
+ console.error("Can't remove login block", e);
+ console.error(blockRequest);
+ return void cb(false);
+ }
+ }));
+ }).nThen(function (waitFor) {
+ rpc.removeOwnedChannel(secret.channel, waitFor(function (e) {
+ if (e) {
+ waitFor.abort();
+ console.error("Can't remove channel", e);
+ return void cb(false);
+ }
+ }));
+ }).nThen(function () {
+ cb(true);
+ });
+
+ }, _alert("Login block is not working (write/read/remove)"));
var row = function (cells) {
return h('tr', cells.map(function (cell) {
@@ -73,6 +213,8 @@ define([
]);
};
+ var completed = 0;
+ var $progress = $('#cp-progress');
assert.run(function (state) {
var errors = state.errors;
var failed = errors.length;
@@ -94,6 +236,17 @@ define([
h('div.failures', errors.map(failureReport)),
]);
+ $progress.remove();
$('body').prepend(report);
+ }, function (i, total) {
+ console.log('test '+ i +' completed');
+ completed++;
+ Messages.assert_numberOfTestsCompleted = "{0} / {1} tests completed.";
+ $progress.html('').append(h('div.report.pending.summary', [
+ h('p', [
+ h('i.fa.fa-spinner.fa-pulse'),
+ h('span', Messages._getKey('assert_numberOfTestsCompleted', [completed, total]))
+ ])
+ ]));
});
});
diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js
index 2084cb1e1..91e5692b1 100644
--- a/www/common/cryptpad-common.js
+++ b/www/common/cryptpad-common.js
@@ -232,17 +232,6 @@ define([
};
postMessage("MIGRATE_ANON_DRIVE", data, cb);
};
- // Settings
- common.deleteAccount = function (cb) {
- postMessage("DELETE_ACCOUNT", null, function (obj) {
- if (obj.state) {
- Feedback.send('DELETE_ACCOUNT_AUTOMATIC');
- } else {
- Feedback.send('DELETE_ACCOUNT_MANUAL');
- }
- cb(obj);
- });
- };
// Drive
common.userObjectCommand = function (data, cb) {
postMessage("DRIVE_USEROBJECT", data, cb);
@@ -1674,30 +1663,16 @@ define([
};
- common.changeUserPassword = function (Crypt, edPublic, data, cb) {
- if (!edPublic) {
- return void cb({
- error: 'E_NOT_LOGGED_IN'
- });
- }
+ var getBlockKeys = function (data, cb) {
var accountName = LocalStore.getAccountName();
- var hash = LocalStore.getUserHash();
- if (!hash) {
- return void cb({
- error: 'E_NOT_LOGGED_IN'
- });
- }
-
- var password = data.password; // To remove your old block
- var newPassword = data.newPassword; // To create your new block
- var secret = Hash.getSecrets('drive', hash);
- var newHash, newHref, newSecret, blockKeys;
- var oldIsOwned = false;
+ var password = data.password;
+ var Cred, Block, Login;
+ var blockKeys;
+ var hash = LocalStore.getUserHash();
+ if (!hash) { return void cb({ error: 'E_NOT_LOGGED_IN' }); }
var blockHash = LocalStore.getBlockHash();
- var oldBlockKeys;
- var Cred, Block, Login;
Nthen(function (waitFor) {
require([
'/common/common-credential.js',
@@ -1710,30 +1685,92 @@ define([
}));
}).nThen(function (waitFor) {
// confirm that the provided password is correct
- Cred.deriveFromPassphrase(accountName, password, Login.requiredBytes, waitFor(function (bytes) {
+ Cred.deriveFromPassphrase(accountName, password, Login.requiredBytes,
+ waitFor(function (bytes) {
var allocated = Login.allocateBytes(bytes);
- oldBlockKeys = allocated.blockKeys;
+ blockKeys = allocated.blockKeys;
if (blockHash) {
if (blockHash !== allocated.blockHash) {
+ // incorrect password
console.log("provided password did not yield the correct blockHash");
- // incorrect password probably
waitFor.abort();
- return void cb({
- error: 'INVALID_PASSWORD',
- });
+ return void cb({ error: 'INVALID_PASSWORD', });
}
- // the user has already created a block, so you should compare against that
} else {
// otherwise they're a legacy user, and we should check against the User_hash
if (hash !== allocated.userHash) {
+ // incorrect password
console.log("provided password did not yield the correct userHash");
waitFor.abort();
- return void cb({
- error: 'INVALID_PASSWORD',
- });
+ return void cb({ error: 'INVALID_PASSWORD', });
}
}
}));
+ }).nThen(function () {
+ cb({
+ Cred: Cred,
+ Block: Block,
+ Login: Login,
+ blockKeys: blockKeys
+ });
+ });
+ };
+ common.deleteAccount = function (data, cb) {
+ data = data || {};
+
+ // Confirm that the provided password is corrct and get the block keys
+ getBlockKeys(data, function (obj) {
+ if (obj && obj.error) { return void cb(obj); }
+ var blockKeys = obj.blockKeys;
+ var removeData = obj.Block.remove(blockKeys);
+
+ postMessage("DELETE_ACCOUNT", {
+ removeData: removeData
+ }, function (obj) {
+ if (obj.state) {
+ Feedback.send('DELETE_ACCOUNT_AUTOMATIC');
+ } else {
+ Feedback.send('DELETE_ACCOUNT_MANUAL');
+ }
+ cb(obj);
+ });
+ });
+ };
+ common.changeUserPassword = function (Crypt, edPublic, data, cb) {
+ if (!edPublic) {
+ return void cb({
+ error: 'E_NOT_LOGGED_IN'
+ });
+ }
+ var accountName = LocalStore.getAccountName();
+ var hash = LocalStore.getUserHash();
+ if (!hash) {
+ return void cb({
+ error: 'E_NOT_LOGGED_IN'
+ });
+ }
+
+ var password = data.password; // To remove your old block
+ var newPassword = data.newPassword; // To create your new block
+ var secret = Hash.getSecrets('drive', hash);
+ var newHash, newHref, newSecret, blockKeys;
+ var oldIsOwned = false;
+
+ var blockHash = LocalStore.getBlockHash();
+ var oldBlockKeys;
+
+ var Cred, Block, Login;
+ Nthen(function (waitFor) {
+ getBlockKeys(data, waitFor(function (obj) {
+ if (obj && obj.error) {
+ waitFor.abort();
+ return void cb(obj);
+ }
+ oldBlockKeys = obj.blockKeys;
+ Cred = obj.Cred;
+ Login = obj.Login;
+ Block = obj.Block;
+ }));
}).nThen(function (waitFor) {
// Check if our drive is already owned
console.log("checking if old drive is owned");
diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js
index 22181c5bb..13064db8a 100644
--- a/www/common/outer/async-store.js
+++ b/www/common/outer/async-store.js
@@ -741,6 +741,7 @@ define([
Store.deleteAccount = function (clientId, data, cb) {
var edPublic = store.proxy.edPublic;
+ var removeData = data && data.removeData;
Store.anonRpcMsg(clientId, {
msg: 'GET_METADATA',
data: store.driveChannel
@@ -769,8 +770,11 @@ define([
channel: store.driveChannel,
force: true
}, waitFor());
+ }).nThen(function (waitFor) {
+ if (!removeData) { return; }
+ // Delete the block. Don't abort if it fails, it doesn't leak any data.
+ store.rpc.removeLoginBlock(removeData, waitFor());
}).nThen(function () {
- // TODO delete block
// Log out current worker
postMessage(clientId, "DELETE_ACCOUNT", token, function () {});
store.network.disconnect();
diff --git a/www/settings/app-settings.less b/www/settings/app-settings.less
index a789ad3e4..3d4577e99 100644
--- a/www/settings/app-settings.less
+++ b/www/settings/app-settings.less
@@ -85,7 +85,7 @@
}
}
- .cp-settings-change-password, .cp-settings-own-drive {
+ .cp-settings-change-password, .cp-settings-own-drive, .cp-settings-delete {
[type="password"], [type="text"] {
width: @sidebar_button-width;
flex: unset;
diff --git a/www/settings/inner.js b/www/settings/inner.js
index 8feef06e4..38969f823 100644
--- a/www/settings/inner.js
+++ b/www/settings/inner.js
@@ -469,63 +469,65 @@ define([
});
}, true);
- create['delete'] = function() {
- if (!common.isLoggedIn()) { return; }
- var $div = $('', { 'class': 'cp-settings-delete cp-sidebarlayout-element' });
-
- $('', { 'class': 'label' }).text(Messages.settings_deleteTitle).appendTo($div);
+ makeBlock('delete', function(cb) { // Msg.settings_deleteHint, .settings_deleteTitle
+ if (!common.isLoggedIn()) { return cb(false); }
- $('', { 'class': 'cp-sidebarlayout-description' })
- .append(Messages.settings_deleteHint).appendTo($div);
-
- var $ok = $('', { 'class': 'fa fa-check', title: Messages.saved });
- var $spinner = $('', { 'class': 'fa fa-spinner fa-pulse' });
-
- var $button = $('