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'); + }); + }); + + +});