diff --git a/customize.dist/src/less2/pages/page-report.less b/customize.dist/src/less2/pages/page-report.less
new file mode 100644
index 000000000..bd2cd6c60
--- /dev/null
+++ b/customize.dist/src/less2/pages/page-report.less
@@ -0,0 +1,17 @@
+@import (reference) "../include/colortheme-all.less";
+@import (reference) "../include/font.less";
+//@import (reference) "../include/forms.less";
+@import (reference) "../include/alertify.less";
+
+html, body {
+ .font_main();
+ .alertify_main();
+ height: 100%;
+ margin: 0px;
+ padding: 0px;
+ background-color: @cp_static-bg !important;
+ color: @cryptpad_text_col;
+ font-family: "IBM Plex Mono";
+}
+
+
diff --git a/www/common/cryptget.js b/www/common/cryptget.js
index 1cbd5056e..a1d0eef92 100644
--- a/www/common/cryptget.js
+++ b/www/common/cryptget.js
@@ -13,7 +13,7 @@ define([
], function (Crypto, CPNetflux, Netflux, Util, Hash, Realtime, NetConfig, Cache, Pinpad, nThen) {
var finish = function (S, err, doc) {
if (S.done) { return; }
- S.cb(err, doc);
+ S.cb((err && err.error), doc, err);
S.done = true;
if (!S.hasNetwork) {
@@ -135,13 +135,15 @@ define([
config.onError = function (info) {
console.warn(info);
- finish(Session, info.error);
+ finish(Session, info);
};
config.onChannelError = function (info) {
console.error(info);
- finish(Session, info.error);
+ finish(Session, info);
};
+ config.onCacheReady = opt.onCacheReady;
+
// We use the new onMessage handler to compute the progress:
// we should receive 2 checkpoints max, so 100 messages max
// We're going to consider that 1 message = 1%, and we'll send 100%
diff --git a/www/report/index.html b/www/report/index.html
new file mode 100644
index 000000000..75956af26
--- /dev/null
+++ b/www/report/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/www/report/main.js b/www/report/main.js
new file mode 100644
index 000000000..367d457a0
--- /dev/null
+++ b/www/report/main.js
@@ -0,0 +1,270 @@
+define([
+ 'jquery',
+ '/api/config',
+ '/common/hyperscript.js',
+ '/customize/messages.js',
+ '/bower_components/nthen/index.js',
+ '/common/common-hash.js',
+ '/common/common-util.js',
+ '/common/cryptget.js',
+ '/common/cryptpad-common.js',
+ '/common/outer/cache-store.js',
+ '/common/common-interface.js',
+ '/bower_components/chainpad-netflux/chainpad-netflux.js',
+ '/bower_components/chainpad-crypto/crypto.js',
+ '/common/userObject.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-report.less',
+], function ($, ApiConfig, h, Messages,
+ nThen, Hash, Util, Crypt, Cryptpad, Cache, UI, CPNetflux,
+ Crypto, UserObject) {
+ var $report = $('#cp-report');
+ var hash = localStorage.User_hash;
+ if (!hash) {
+ return void UI.errorLoadingScreen(Messages.mustLogin);
+ }
+
+ var addReport = function (str) {
+ $report.append(h('div', str));
+ };
+ var checkCache = function (chan, cb) {
+ Cache.getChannelCache(chan, function (err, val) {
+ if (err) {
+ addReport('Cache error:' + err);
+ } else {
+ addReport('Cache: ' + val.c.length + ' entries');
+ }
+ cb();
+ });
+ };
+ var onCacheReady = function (info) {
+ var doc;
+ try {
+ doc = info.realtime.getUserDoc();
+ JSON.parse(doc);
+ addReport('Cache ready success: ' + info.id + ' - Length: ' + doc.length);
+ } catch (e) {
+ addReport('Cache ready error: ' + info.id + ' - Length: ' + (doc || '').length);
+ }
+ };
+
+ var network;
+ var proxy;
+ nThen(function (waitFor) {
+ Cryptpad.makeNetwork(waitFor(function (err, _network) {
+ if (err) {
+ console.error(err);
+ waitFor.abort();
+ return void UI.errorLoadingScreen(err);
+ }
+ network = _network;
+ }));
+ }).nThen(function (waitFor) {
+ var secret = Hash.getSecrets('drive', hash);
+ addReport('Load drive. ID: ' + secret.channel);
+ checkCache(secret.channel, waitFor());
+ }).nThen(function (waitFor) {
+ Crypt.get(hash, waitFor(function (err, val) {
+ if (err) {
+ console.error(err);
+ addReport('Load drive error. err: ' + err);
+ return void waitFor.abort();
+ }
+ try {
+ proxy = JSON.parse(val);
+ } catch (e) {
+ console.error(e);
+ addReport('Load drive error. Parse error: ' + e);
+ waitFor.abort();
+ }
+ }), { network: network, onCacheReady: onCacheReady});
+ }).nThen(function (waitFor) {
+ console.log(proxy);
+ var drive = proxy.drive || {};
+ if (!proxy.drive) {
+ addReport('ERROR: no drive');
+ return void waitFor.abort();
+ }
+ addReport('Load drive success.');
+ addReport('Public key: '+proxy.edPublic);
+ addReport('Shared folders: ' + Object.keys(drive.sharedFolders || {}).join(', '));
+ addReport('Teams: ' + Object.keys(proxy.teams || {}).join(', '));
+ addReport('-------------------');
+
+ var n = nThen;
+ Object.keys(drive.sharedFolders || {}).forEach(function (id) {
+ n = n(function (w) {
+ var next = w();
+ var obj = drive.sharedFolders[id];
+ addReport('Load shared folder. ID: ' + id + '. Channel ID: '+ obj.channel);
+ if (obj.password) { addReport("Password protected"); }
+ if (!obj.href) { addReport("View only"); }
+ checkCache(obj.channel, function () {
+ var parsed = Hash.parsePadUrl(obj.href || obj.roHref);
+ Crypt.get(parsed.hash, function (err, val, errorObj) {
+ if (err) {
+ addReport('ERROR: ' + err);
+ if (err === "ERESTRICTED") {
+ addReport('RESTRICTED: ' + (errorObj && errorObj.message));
+ }
+ } else {
+ addReport('Load shared folder: success. Size: ' + val.length);
+ }
+ addReport('-------------------');
+ next();
+ }, {
+ network: network,
+ password: obj.password,
+ onCacheReady: onCacheReady
+ });
+
+ });
+ }).nThen;
+ });
+ n(waitFor());
+ }).nThen(function () {
+ addReport('===================');
+ var n = nThen;
+ Object.keys(proxy.teams || {}).forEach(function (id) {
+ n = n(function (w) {
+ var next = w();
+ var obj = proxy.teams[id];
+ var team;
+ addReport('Load team. ID: ' + id + '. Channel ID: '+ obj.channel);
+ if (!obj.hash) { addReport("View only"); }
+
+ var teamSecret = Hash.getSecrets('team', obj.hash || obj.roHash, obj.password);
+ var cryptor = UserObject.createCryptor(teamSecret.keys.secondaryKey);
+
+ // Check team drive
+ nThen(function (ww) {
+ addReport('Team drive');
+ var _next = ww();
+ checkCache(obj.channel, function () {
+ Crypt.get(obj.hash || obj.roHash, function (err, val) {
+ if (err) {
+ addReport('ERROR: ' + err);
+ addReport('===================');
+ next();
+ ww.abort();
+ } else {
+ addReport('Team drive success. Size: ' + val.length);
+ try {
+ team = JSON.parse(val);
+ } catch (e) {
+ addReport('PARSE ERROR: ' + e);
+ addReport('===================');
+ next();
+ ww.abort();
+ }
+ addReport('Shared folders: ' + Object.keys(team.drive.sharedFolders || {}).join(', '));
+ }
+ addReport('-------------------');
+ _next();
+ }, {
+ network: network,
+ password: obj.password,
+ onCacheReady: onCacheReady
+ });
+ });
+ }).nThen(function (ww) {
+ var _next = ww();
+ var d = Util.find(obj, ['keys', 'roster']);
+
+ var rosterKeys = d.edit ? Crypto.Team.deriveMemberKeys(d.edit, proxy)
+ : Crypto.Team.deriveGuestKeys(d.view || '');
+
+ if (d.channel !== rosterKeys.channel) {
+ next();
+ ww.abort();
+ addReport("Invalid roster keys:", d.channel, rosterKeys.channel);
+ return;
+ }
+ addReport('Roster channel: ' + d.channel);
+ console.warn(rosterKeys);
+ checkCache(d.channel, function () {
+ var crypto = Crypto.Team.createEncryptor(rosterKeys);
+ var m = 0;
+ CPNetflux.start({
+ lastKnownHash: d.lastKnownHash || -1,
+ network: network,
+ channel: d.channel,
+ crypto: crypto,
+ validateKey: rosterKeys.teamEdPublic,
+ Cache: Cache,
+ noChainPad: true,
+ onCacheReady: onCacheReady,
+ onChannelError: function (obj) {
+ addReport('ERROR:' + obj.error);
+ if (obj.error === "ERESTRICTED") {
+ addReport('RESTRICTED: ' + obj.message);
+ }
+ next();
+ ww.abort();
+ addReport('===================');
+ return;
+ },
+ onMessage: function () {
+ m++;
+ },
+ onReady: function () {
+ addReport("Roster success. Length: "+m);
+ addReport('-------------------');
+ _next();
+ }
+ });
+ });
+ }).nThen(function (ww) {
+ var _next = ww();
+ var n = nThen;
+ var drive = team.drive;
+ Object.keys(drive.sharedFolders || {}).forEach(function (id) {
+ n = n(function (w) {
+ var next = w();
+ var _obj = drive.sharedFolders[id];
+ addReport('Load shared folder. ID: ' + id + '. Channel ID: '+ _obj.channel);
+ if (_obj.password) { addReport("Password protected"); }
+ if (!_obj.href) { addReport("View only"); }
+ checkCache(_obj.channel, function () {
+ if (_obj.href && _obj.href.indexOf('#') === -1) {
+ _obj.href = cryptor.decrypt(_obj.href);
+ }
+ var parsed = Hash.parsePadUrl(_obj.href || _obj.roHref);
+ console.log(parsed.hash, _obj);
+ Crypt.get(parsed.hash, function (err, val, errorObj) {
+ if (err) {
+ addReport('ERROR: ' + err);
+ if (err === "ERESTRICTED") {
+ addReport('RESTRICTED: ' + (errorObj && errorObj.message));
+ }
+ } else {
+ addReport('Load shared folder: success. Size: ' + val.length);
+ }
+ addReport('-------------------');
+ next();
+ }, {
+ network: network,
+ password: _obj.password,
+ onCacheReady: onCacheReady
+ });
+
+ });
+ }).nThen;
+ });
+ n(_next);
+ }).nThen(next);
+
+
+ }).nThen;
+ });
+ n(function () {
+ addReport('===================');
+ addReport('DONE');
+ });
+ });
+
+
+});