diff --git a/www/common/common-hash.js b/www/common/common-hash.js index d7d95e78a..d62204ac4 100644 --- a/www/common/common-hash.js +++ b/www/common/common-hash.js @@ -6,7 +6,7 @@ define([ ], function (Util, Messages, Crypto) { var Nacl = window.nacl; - var Hash = {}; + var Hash = window.CryptPad_Hash = {}; var uint8ArrayToHex = Util.uint8ArrayToHex; var hexToBase64 = Util.hexToBase64; diff --git a/www/common/common-util.js b/www/common/common-util.js index 030536751..98eda7373 100644 --- a/www/common/common-util.js +++ b/www/common/common-util.js @@ -1,6 +1,6 @@ (function (window) { define([], function () { - var Util = {}; + var Util = window.CryptPad_Util = {}; // If once is true, after the event has been fired, any further handlers which are // registered will fire immediately, and this type of event cannot be fired twice. diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 5ec198a44..214939072 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -196,6 +196,13 @@ define([ postMessage("CLEAR_OWNED_CHANNEL", channel, cb); }; + common.getDeletedPads = function (cb) { + postMessage("GET_DELETED_PADS", null, function (obj) { + if (obj && obj.error) { return void cb(obj.error); } + cb(null, obj); + }); + }; + common.uploadComplete = function (cb) { postMessage("UPLOAD_COMPLETE", null, function (obj) { if (obj && obj.error) { return void cb(obj.error); } diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index e00500472..5c135a3fb 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -94,9 +94,27 @@ define([ return list; }; - var getCanonicalChannelList = function () { - return Util.deduplicateString(getUserChannelList()).sort(); + var getExpirableChannelList = function () { + var list = []; + store.userObject.getFiles([store.userObject.FILES_DATA]).forEach(function (id) { + var data = store.userObject.getFileData(id); + var edPublic = store.proxy.edPublic; + + // Push channels owned by someone else or channel that should have expired + // because of the expiration time + if ((data.owners && data.owners.indexOf(edPublic) === -1) || + data.expire < (+new Date())) { + list.push(Hash.hrefToHexChannelId(data.href)); + } + }); + return list; + }; + + var getCanonicalChannelList = function (expirable) { + var list = expirable ? getExpirableChannelList() : getUserChannelList(); + return Util.deduplicateString(list).sort(); }; + ////////////////////////////////////////////////////////////////// /////////////////////// RPC ////////////////////////////////////// ////////////////////////////////////////////////////////////////// @@ -172,6 +190,37 @@ define([ }); }; + var arePinsSynced = function (cb) { + if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } + + var list = getCanonicalChannelList(false); + var local = Hash.hashChannelList(list); + store.rpc.getServerHash(function (e, hash) { + if (e) { return void cb(e); } + cb(null, hash === local); + }); + }; + + var resetPins = function (cb) { + if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } + + var list = getCanonicalChannelList(false); + store.rpc.reset(list, function (e, hash) { + if (e) { return void cb(e); } + cb(null, hash); + }); + }; + + Store.getDeletedPads = function (data, cb) { + if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } + + //var list = getCanonicalChannelList(true); + + // TODO: rpc to get the deleted pads here and send this list in the callback + + cb([]); + }; + Store.uploadComplete = function (data, cb) { if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } store.rpc.uploadComplete(function (err, res) { @@ -196,27 +245,6 @@ define([ }); }; - var arePinsSynced = function (cb) { - if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } - - var list = getCanonicalChannelList(); - var local = Hash.hashChannelList(list); - store.rpc.getServerHash(function (e, hash) { - if (e) { return void cb(e); } - cb(null, hash === local); - }); - }; - - var resetPins = function (cb) { - if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); } - - var list = getCanonicalChannelList(); - store.rpc.reset(list, function (e, hash) { - if (e) { return void cb(e); } - cb(null, hash); - }); - }; - Store.uploadChunk = function (data, cb) { store.rpc.send.unauthenticated('UPLOAD', data.chunk, function (e, msg) { cb({ @@ -579,6 +607,8 @@ define([ contains = true; pad.atime = +new Date(); pad.title = title; + pad.owners = owners; + pad.expire = expire; // If the href is different, it means we have a stronger one if (href !== pad.href) { isStronger = true; } diff --git a/www/common/outer/chainpad-netflux-worker.js b/www/common/outer/chainpad-netflux-worker.js index f136e2a1e..5c07cd93f 100644 --- a/www/common/outer/chainpad-netflux-worker.js +++ b/www/common/outer/chainpad-netflux-worker.js @@ -57,27 +57,11 @@ define([], function () { // shim between chainpad and netflux var msgIn = function (peerId, msg) { return msg.replace(/^cp\|/, ''); - - /*try { - var decryptedMsg = Crypto.decrypt(msg, validateKey); - return decryptedMsg; - } catch (err) { - console.error(err); - return msg; - }*/ }; var msgOut = function (msg) { if (readOnly) { return; } return msg; - /*try { - var cmsg = Crypto.encrypt(msg); - if (msg.indexOf('[4') === 0) { cmsg = 'cp|' + cmsg; } - return cmsg; - } catch (err) { - console.log(msg); - throw err; - }*/ }; var onMsg = function(peer, msg, wc, network, direct) { @@ -93,7 +77,9 @@ define([], function () { if (parsed.channel === wc.id && !validateKey) { validateKey = parsed.validateKey; } - padData = parsed; + if (parsed.channel === wc.id) { + padData = parsed; + } // We have to return even if it is not the current channel: // we don't want to continue with other channels messages here return; diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index 39895b68c..c1b69dab7 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -49,6 +49,9 @@ define([ case 'UNPIN_PADS': { Store.unpinPads(data, cb); break; } + case 'GET_DELETED_PADS': { + Store.getDeletedPads(data, cb); break; + } case 'GET_PINNED_USAGE': { Store.getPinnedUsage(data, cb); break; } diff --git a/www/common/sframe-protocol.js b/www/common/sframe-protocol.js index 96014c992..5035e2af2 100644 --- a/www/common/sframe-protocol.js +++ b/www/common/sframe-protocol.js @@ -201,6 +201,8 @@ define({ // Inner drive needs to send command and receive updates from the async store 'Q_DRIVE_USEROBJECT': true, 'Q_DRIVE_GETOBJECT': true, + // Get the pads deleted from the server by other users to remove them from the drive + 'Q_DRIVE_GETDELETED': true, // Store's userObject need to send log messages to inner to display them in the UI 'EV_DRIVE_LOG': true, // Refresh the drive when the drive has changed ('change' or 'remove' events) diff --git a/www/common/userObject.js b/www/common/userObject.js index ecead9f2b..223376963 100644 --- a/www/common/userObject.js +++ b/www/common/userObject.js @@ -380,6 +380,18 @@ define([ var trashpaths = _findFileInTrash([TRASH], file); return rootpaths.concat(templatepaths, trashpaths); }; + + // Get drive ids of files from their channel ids + exp.findChannels = function (channels) { + var allFilesList = files[FILES_DATA]; + var channels64 = channels.slice().map(Util.hexToBase64); + return getFiles([FILES_DATA]).filter(function (k) { + var data = allFilesList[k]; + var parsed = Hash.parsePadUrl(data.href); + return parsed.hashData && channels64.indexOf(parsed.hashData.channel) !== -1; + }); + }; + exp.search = function (value) { if (typeof(value) !== "string") { return []; } value = value.trim(); diff --git a/www/drive/inner.js b/www/drive/inner.js index b2a1a774b..afdf8165c 100644 --- a/www/drive/inner.js +++ b/www/drive/inner.js @@ -2975,6 +2975,14 @@ define([ refresh(); UI.removeLoadingScreen(); + + sframeChan.query('Q_DRIVE_GETDELETED', null, function (err, data) { + var ids = filesOp.findChannels(data); + ids.forEach(function (id) { + var paths = filesOp.findFile(id); + filesOp.delete(paths, refresh); + }); + }); }; var setHistory = function (bool, update) { @@ -3091,7 +3099,6 @@ define([ throw new Error("Corrupted drive"); } andThen(common, proxy); - UI.removeLoadingScreen(); var onDisconnect = APP.onDisconnect = function (noAlert) { setEditable(false); diff --git a/www/drive/main.js b/www/drive/main.js index 552a6a0c6..61afafb2f 100644 --- a/www/drive/main.js +++ b/www/drive/main.js @@ -56,6 +56,12 @@ define([ cb(obj); }); }); + sframeChan.on('Q_DRIVE_GETDELETED', function (data, cb) { + Cryptpad.getDeletedPads(function (err, obj) { + if (err) { return void console.error(err); } + cb(obj); + }); + }); Cryptpad.onNetworkDisconnect.reg(function () { sframeChan.event('EV_NETWORK_DISCONNECT'); });