diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 9e65dba93..2ac4101f7 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -1006,6 +1006,10 @@ define([ postMessage("GIVE_PAD_ACCESS", data, cb); }; + common.onCorruptedCache = function (channel) { + postMessage("CORRUPTED_CACHE", channel); + }; + common.setPadMetadata = function (data, cb) { postMessage('SET_PAD_METADATA', data, cb); }; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index ec9c36d14..c91106d8c 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -1610,7 +1610,7 @@ define([ if (padData && padData.validateKey && store.messenger) { store.messenger.storeValidateKey(data.channel, padData.validateKey); } - postMessage(clientId, "PAD_READY"); + postMessage(clientId, "PAD_READY", pad.noCache); }, onMessage: function (m, user, validateKey, isCp, hash) { channel.lastHash = hash; @@ -1741,6 +1741,14 @@ define([ channel.sendMessage(msg, clientId, cb); }; + Store.corruptedCache = function (clientId, channel) { + var chan = channels[channel]; + if (!chan || !chan.cpNf) { return; } + Cache.clearChannel(channel); + if (!chan.cpNf.resetCache) { return; } + chan.cpNf.resetCache(); + }; + // Unpin and pin the new channel in all team when changing a pad password Store.changePadPasswordPin = function (clientId, data, cb) { var oldChannel = data.oldChannel; diff --git a/www/common/outer/cache-store.js b/www/common/outer/cache-store.js index aed480860..56c78a5be 100644 --- a/www/common/outer/cache-store.js +++ b/www/common/outer/cache-store.js @@ -1,7 +1,7 @@ define([ - // XXX Load util and use mkAsync + '/common/common-util.js', '/bower_components/localforage/dist/localforage.min.js', -], function (localForage) { +], function (Util, localForage) { var S = {}; var cache = localForage.createInstance({ @@ -11,43 +11,50 @@ define([ // id: channel ID or blob ID // returns array of messages S.getChannelCache = function (id, cb) { + cb = Util.once(Util.mkAsync(cb || function () {})); cache.getItem(id, function (err, obj) { if (err || !obj || !Array.isArray(obj.c)) { return void cb(err || 'EINVAL'); } cb(null, obj); + obj.t = +new Date(); + cache.setItem(id, obj); }); }; + // Keep the last two checkpoint + any checkpoint that may exist in the last 100 messages + // FIXME: duplicate system with sliceCpIndex from lib/hk-util.js var checkCheckpoints = function (array) { if (!Array.isArray(array)) { return; } - var cp = 0; - // XXX check sliceCpIndex from hk-util: use the same logic for forks - for (var i = array.length - 1; i >= 0; i--) { - if (array[i].isCheckpoint) { cp++; } - if (cp === 2) { - array.splice(0, i); - break; - } + // Keep the last 100 messages + if (array.length > 100) { + array.splice(0, array.length - 100); } + // Remove every message before the first checkpoint + var firstCpIdx; + array.some(function (el, i) { + if (!el.isCheckpoint) { return; } + firstCpIdx = i; + return true; + }); + array.splice(0, firstCpIdx); }; S.storeCache = function (id, validateKey, val, cb) { - cb = cb || function () {}; + cb = Util.once(Util.mkAsync(cb || function () {})); if (!Array.isArray(val) || !validateKey) { return void cb('EINVAL'); } checkCheckpoints(val); cache.setItem(id, { k: validateKey, - c: val - // XXX add "time" here +new Date() ==> we want to store the time when it was updated in our indexeddb in case we want to remove inactive entries from indexeddb later - // XXX we would also need to update the time when we "getChannelCache" + c: val, + t: (+new Date()) // 't' represent the "lastAccess" of this cache (get or set) }, function (err) { cb(err); }); }; - S.clearChannel = function (id, cb) { - cb = cb || function () {}; + S.clearChannel = function (id, _cb) { + cb = Util.once(Util.mkAsync(_cb || function () {})); cache.removeItem(id, cb); }; diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index 6ac3e52b0..767448284 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -88,6 +88,7 @@ define([ CHANGE_PAD_PASSWORD_PIN: Store.changePadPasswordPin, GET_LAST_HASH: Store.getLastHash, GET_SNAPSHOT: Store.getSnapshot, + CORRUPTED_CACHE: Store.corruptedCache, // Drive DRIVE_USEROBJECT: Store.userObjectCommand, // Settings, diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 24f07e48e..6353fb2f8 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -467,6 +467,10 @@ define([ }); }; + var onCorruptedCache = function (cb) { + var sframeChan = common.getSframeChannel(); + sframeChan.event("Q_CORRUPTED_CACHE", cb); + }; var onCacheReady = function () { stateChange(STATE.DISCONNECTED); toolbar.offline(true); @@ -475,11 +479,9 @@ define([ // Check if we have a new chainpad instance toolbar.resetChainpad(cpNfInner.chainpad); } -console.log(newContentStr); - // Invalid cache? abort - // XXX tell outer/worker to invalidate cache - if (newContentStr === '') { return; } + // Invalid cache + if (newContentStr === '') { return void onCorruptedCache(); } var privateDat = cpNfInner.metadataMgr.getPrivateData(); var type = privateDat.app; @@ -489,8 +491,7 @@ console.log(newContentStr); // Make sure we're using the correct app for this cache if (metadata && typeof(metadata.type) !== 'undefined' && metadata.type !== type) { - console.error('return'); - return; + return void onCorruptedCache(); } cpNfInner.metadataMgr.updateMetadata(metadata); @@ -503,7 +504,7 @@ console.log(newContentStr); }; var onReady = function () { toolbar.offline(false); - console.error('READY'); + var newContentStr = cpNfInner.chainpad.getUserDoc(); if (state === STATE.DELETED) { return; } @@ -544,7 +545,12 @@ console.log(newContentStr); console.log("Either this is an empty document which has not been touched"); console.log("Or else something is terribly wrong, reloading."); Feedback.send("NON_EMPTY_NEWDOC"); - setTimeout(function () { common.gotoURL(); }, 1000); + // The cache may be wrong, empty it and reload after. + waitFor.abort(); + UI.errorLoadingScreen("MAYBE CORRUPTED CACHE... RELOADING"); // XXX + onCorruptedCache(function () { + setTimeout(function () { common.gotoURL(); }, 1000); + }); return; } console.log('updating title'); diff --git a/www/common/sframe-chainpad-netflux-inner.js b/www/common/sframe-chainpad-netflux-inner.js index 08625dc77..1bdcc260f 100644 --- a/www/common/sframe-chainpad-netflux-inner.js +++ b/www/common/sframe-chainpad-netflux-inner.js @@ -94,13 +94,8 @@ define([ evInfiniteSpinner.fire(); }, 2000); - sframeChan.on('EV_RT_CACHE', function () { - // XXX - }); sframeChan.on('EV_RT_CACHE_READY', function () { - // XXX onCacheReady({realtime: chainpad}); - console.error('PEWPEWPEW'); }); sframeChan.on('EV_RT_DISCONNECT', function (isPermanent) { isReady = false; @@ -143,7 +138,6 @@ define([ evConnected.fire(); }); sframeChan.on('Q_RT_MESSAGE', function (content, cb) { - console.log(content); if (isReady) { onLocal(true); // should be onBeforeMessage } diff --git a/www/common/sframe-chainpad-netflux-outer.js b/www/common/sframe-chainpad-netflux-outer.js index 056b9cd28..c19299da2 100644 --- a/www/common/sframe-chainpad-netflux-outer.js +++ b/www/common/sframe-chainpad-netflux-outer.js @@ -89,7 +89,6 @@ define([], function () { validateKey = msgObj.validateKey; } var message = msgIn(msgObj.user, msgObj.msg); - console.log(message); if (!message) { return; } lastTime = msgObj.time; @@ -127,7 +126,6 @@ define([], function () { }); padRpc.onCacheReadyEvent.reg(function () { - console.log('ONCACHEREADY'); sframeChan.event('EV_RT_CACHE_READY'); }); diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index b71fbc249..fe32a6f8a 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -53,12 +53,13 @@ define([ '/common/common-constants.js', '/common/common-feedback.js', '/common/outer/local-store.js', + '/common/outer/cache-store.js', '/customize/application_config.js', '/common/test.js', '/common/userObject.js', ], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel, _SecureIframe, _Messaging, _Notifier, _Hash, _Util, _Realtime, - _Constants, _Feedback, _LocalStore, _AppConfig, _Test, _UserObject) { + _Constants, _Feedback, _LocalStore, _Cache, _AppConfig, _Test, _UserObject) { CpNfOuter = _CpNfOuter; Cryptpad = _Cryptpad; Crypto = Utils.Crypto = _Crypto; @@ -73,6 +74,7 @@ define([ Utils.Constants = _Constants; Utils.Feedback = _Feedback; Utils.LocalStore = _LocalStore; + Utils.Cache = _Cache; Utils.UserObject = _UserObject; AppConfig = _AppConfig; Test = _Test; @@ -1636,6 +1638,11 @@ define([ }); }; + sframeChan.on('Q_CORRUPTED_CACHE', function (data, cb) { + Utils.Cache.clearChannel(secret.channel, cb); + Cryptpad.onCorruptedCache(secret.channel); + }); + sframeChan.on('Q_CREATE_PAD', function (data, cb) { if (!isNewFile || rtStarted) { return; } // Create a new hash