From 52712c4bb95c2ce7a0924f879d523d2c5be5d599 Mon Sep 17 00:00:00 2001 From: yflory Date: Mon, 4 Dec 2017 19:16:38 +0100 Subject: [PATCH] Use the network from the async store for pads --- www/common/cryptpad-common.js | 31 +++- www/common/outer/async-store.js | 65 ++++++- www/common/outer/store-rpc.js | 7 + www/common/outer/store-worker.js | 2 + www/common/sframe-chainpad-netflux-outer.js | 187 +++----------------- www/common/sframe-common-outer.js | 2 +- www/worker/inner.js | 5 +- www/worker/worker.js | 2 + 8 files changed, 135 insertions(+), 166 deletions(-) diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index c852b86f6..7cdc169ec 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -486,7 +486,20 @@ define([ messenger.onFriendEvent = Util.mkEvent(); messenger.onUnfriendEvent = Util.mkEvent(); - // HERE + // Pad RPC + var pad = common.padRpc = {}; + pad.joinPad = function (data, cb) { + postMessage("JOIN_PAD", data, cb); + }; + pad.sendPadMsg = function (data, cb) { + postMessage("SEND_PAD_MSG", data, cb); + }; + pad.onReadyEvent = Util.mkEvent(); + pad.onMessageEvent = Util.mkEvent(); + pad.onJoinEvent = Util.mkEvent(); + pad.onLeaveEvent = Util.mkEvent(); + pad.onDisconnectEvent = Util.mkEvent(); + common.getShareHashes = function (secret, cb) { var hashes; if (!window.location.hash) { @@ -597,6 +610,22 @@ define([ case 'CONTACTS_UNFRIEND': { common.messenger.onUnfriendEvent.fire(data); break; } + // Pad + case 'PAD_READY': { + common.padRpc.onReadyEvent.fire(); break; + } + case 'PAD_MESSAGE': { + common.padRpc.onMessageEvent.fire(data); break; + } + case 'PAD_JOIN': { + common.padRpc.onJoinEvent.fire(data); break; + } + case 'PAD_LEAVE': { + common.padRpc.onLeaveEvent.fire(data); break; + } + case 'PAD_DISCONNECT': { + common.padRpc.onDisconnectEvent.fire(data); break; + } } }; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index 7c2be3070..eb222d1a2 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -8,13 +8,14 @@ define([ '/common/common-realtime.js', '/common/common-messaging.js', '/common/common-messenger.js', + '/common/outer/chainpad-netflux-worker.js', '/common/outer/network-config.js', '/bower_components/chainpad-crypto/crypto.js?v=0.1.5', '/bower_components/chainpad/chainpad.dist.js', '/bower_components/chainpad-listmap/chainpad-listmap.js', ], function (UserObject, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Messenger, - NetConfig, + CpNfWorker, NetConfig, Crypto, ChainPad, Listmap) { var Store = {}; @@ -95,7 +96,6 @@ define([ var getCanonicalChannelList = function () { return Util.deduplicateString(getUserChannelList()).sort(); }; - ////////////////////////////////////////////////////////////////// /////////////////////// RPC ////////////////////////////////////// ////////////////////////////////////////////////////////////////// @@ -715,6 +715,66 @@ define([ } }; + ////////////////////////////////////////////////////////////////// + /////////////////////// PAD ////////////////////////////////////// + ////////////////////////////////////////////////////////////////// + + // TODO with sharedworker + // channel will be an object storing the webchannel associated to each browser tab + var channel = { + queue: [] + }; + Store.joinPad = function (data, cb) { + var conf = { + onReady: function () { + postMessage("PAD_READY"); + }, // post EV_PAD_READY + onMessage: function (m) { + postMessage("PAD_MESSAGE", m); + }, // post EV_PAD_MESSAGE + onJoin: function (m) { + postMessage("PAD_JOIN", m); + }, // post EV_PAD_JOIN + onLeave: function (m) { + postMessage("PAD_LEAVE", m); + }, // post EV_PAD_LEAVE + onDisconnect: function () { + postMessage("PAD_DISCONNECT"); + }, // post EV_PAD_DISCONNECT + channel: data.channel, + validateKey: data.validateKey, + network: store.network, + readOnly: data.readOnly, + onConnect: function (wc, sendMessage) { + channel.sendMessage = sendMessage; + channel.wc = wc; + channel.queue.forEach(function (data) { + sendMessage(data.message); + }); + cb({ + myID: wc.myID, + id: wc.id, + members: wc.members + }); + } + }; + CpNfWorker.start(conf); + }; + Store.sendPadMsg = function (data, cb) { + if (!channel.wc) { channel.queue.push(data); } + channel.sendMessage(data, cb); + }; + + // TODO + // GET_FULL_HISTORY from sframe-common-outer + + // TODO with sharedworker + // when the tab is closed, leave the pad + + ////////////////////////////////////////////////////////////////// + /////////////////////// Init ///////////////////////////////////// + ////////////////////////////////////////////////////////////////// + var onReady = function (returned, cb) { var proxy = store.proxy; var userObject = store.userObject = UserObject.init(proxy.drive, { @@ -815,6 +875,7 @@ define([ store.proxy = rt.proxy; store.loggedIn = typeof(data.userHash) !== "undefined"; + var returned = {}; rt.proxy.on('create', function (info) { store.realtime = info.realtime; store.network = info.network; diff --git a/www/common/outer/store-rpc.js b/www/common/outer/store-rpc.js index c0b435079..e1db437ec 100644 --- a/www/common/outer/store-rpc.js +++ b/www/common/outer/store-rpc.js @@ -146,6 +146,13 @@ define([ case 'CONTACTS_SET_CHANNEL_HEAD': { Store.messenger.setChannelHead(data, cb); break; } + // Pad + case 'SEND_PAD_MSG': { + Store.sendPadMsg(data, cb); break; + } + case 'JOIN_PAD': { + Store.joinPad(data, cb); break; + } default: { break; diff --git a/www/common/outer/store-worker.js b/www/common/outer/store-worker.js index c34a7b20c..a4b4d159d 100644 --- a/www/common/outer/store-worker.js +++ b/www/common/outer/store-worker.js @@ -1,3 +1,5 @@ +/* jshint ignore:start */ + var window = self; importScripts('/bower_components/requirejs/require.js'); diff --git a/www/common/sframe-chainpad-netflux-outer.js b/www/common/sframe-chainpad-netflux-outer.js index b62a25a05..8ca920bbe 100644 --- a/www/common/sframe-chainpad-netflux-outer.js +++ b/www/common/sframe-chainpad-netflux-outer.js @@ -27,32 +27,17 @@ define([], function () { var Crypto = conf.crypto; var validateKey = conf.validateKey; var readOnly = conf.readOnly || false; - var network = conf.network; + var padRpc = conf.padRpc; var sframeChan = conf.sframeChan; var onConnect = conf.onConnect || function () { }; conf = undefined; - var initializing = true; - var lastKnownHash; - - var queue = []; - var messageFromInner = function (m, cb) { queue.push([ m, cb ]); }; - sframeChan.on('Q_RT_MESSAGE', function (message, cb) { - messageFromInner(message, cb); - }); - - var onReady = function () { - // Trigger onReady only if not ready yet. This is important because the history keeper sends a direct - // message through "network" when it is synced, and it triggers onReady for each channel joined. - if (!initializing) { return; } + padRpc.onReadyEvent.reg(function () { sframeChan.event('EV_RT_READY', null); - // we're fully synced - initializing = false; - }; + }); // shim between chainpad and netflux - var msgIn = function (peerId, msg) { - msg = msg.replace(/^cp\|/, ''); + var msgIn = function (msg) { try { var decryptedMsg = Crypto.decrypt(msg, validateKey); return decryptedMsg; @@ -74,44 +59,14 @@ define([], function () { } }; - var onMessage = function(peer, msg, wc, network, direct) { - // unpack the history keeper from the webchannel - var hk = network.historyKeeper; - - if (direct && peer !== hk) { - return; - } - if (direct) { - var parsed = JSON.parse(msg); - if (parsed.validateKey && parsed.channel) { - if (parsed.channel === wc.id && !validateKey) { - validateKey = parsed.validateKey; - } - // We have to return even if it is not the current channel: - // we don't want to continue with other channels messages here - return; - } - if (parsed.state && parsed.state === 1 && parsed.channel) { - if (parsed.channel === wc.id) { - onReady(wc); - } - // We have to return even if it is not the current channel: - // we don't want to continue with other channels messages here - return; - } - } - // The history keeper is different for each channel : - // no need to check if the message is related to the current channel - if (peer === hk) { - // if the peer is the 'history keeper', extract their message - var parsed1 = JSON.parse(msg); - msg = parsed1[4]; - // Check that this is a message for us - if (parsed1[3] !== wc.id) { return; } - } + sframeChan.on('Q_RT_MESSAGE', function (message, cb) { + var msg = msgOut(message); + if (!msg) { return; } + padRpc.sendPadMsg(msg, cb); + }); - lastKnownHash = msg.slice(0,64); - var message = msgIn(peer, msg); + var onMessage = function(msg) { + var message = msgIn(msg); verbose(message); @@ -124,118 +79,32 @@ define([], function () { sframeChan.query('Q_RT_MESSAGE', message, function () { }); }; - // We use an object to store the webchannel so that we don't have to push new handlers to chainpad - // and remove the old ones when reconnecting and keeping the same 'realtime' object - // See realtime.onMessage below: we call wc.bcast(...) but wc may change - var wcObject = {}; - var onOpen = function(wc, network, firstConnection) { - wcObject.wc = wc; - channel = wc.id; - - onConnect(wc); - onConnect = function () { }; - + var onOpen = function(data) { // Add the existing peers in the userList - sframeChan.event('EV_RT_CONNECT', { myID: wc.myID, members: wc.members, readOnly: readOnly }); + console.log(data); + onConnect(data.id); + onConnect = function () {}; - // Add the handlers to the WebChannel - wc.on('message', function (msg, sender) { //Channel msg - onMessage(sender, msg, wc, network); - }); - wc.on('join', function (m) { sframeChan.event('EV_RT_JOIN', m); }); - wc.on('leave', function (m) { sframeChan.event('EV_RT_LEAVE', m); }); - - if (firstConnection) { - // Sending a message... - messageFromInner = function(message, cb) { - // Filter messages sent by Chainpad to make it compatible with Netflux - message = msgOut(message); - if (message) { - // Do not remove wcObject, it allows us to use a new 'wc' without changing the handler if we - // want to keep the same chainpad (realtime) object - try { - if (window.Cryptpad_SUPPRESS_MSG) { return; } - wcObject.wc.bcast(message).then(function() { - if (window.Cryptpad_SUPPRESS_ACK) { return; } - cb(); - }, function(err) { - // The message has not been sent, display the error. - console.error(err); - }); - } catch (e) { - console.log(e); - // Just skip calling back and it will fail on the inside. - } - } - }; - queue.forEach(function (arr) { messageFromInner(arr[0], arr[1]); }); - } + sframeChan.event('EV_RT_CONNECT', { myID: data.myID, members: data.members, readOnly: readOnly }); - // Get the channel history - if (USE_HISTORY) { - var hk; - - wc.members.forEach(function (p) { - if (p.length === 16) { hk = p; } - }); - network.historyKeeper = hk; - - var msg = ['GET_HISTORY', wc.id]; - // Add the validateKey if we are the channel creator and we have a validateKey - msg.push(validateKey); - msg.push(lastKnownHash); - if (hk) { network.sendto(hk, JSON.stringify(msg)); } - } else { - onReady(wc); - } - }; - - var isIntentionallyLeaving = false; - window.addEventListener("beforeunload", function () { - isIntentionallyLeaving = true; - }); - - var findChannelById = function (webChannels, channelId) { - var webChannel; - - // Array.some terminates once a truthy value is returned - // best case is faster than forEach, though webchannel arrays seem - // to consistently have a length of 1 - webChannels.some(function(chan) { - if(chan.id === channelId) { webChannel = chan; return true;} - }); - return webChannel; - }; - - var connectTo = function (network, firstConnection) { - // join the netflux network, promise to handle opening of the channel - network.join(channel || null).then(function(wc) { - onOpen(wc, network, firstConnection); - }, function(error) { - console.error(error); - }); + // Add the handlers to the WebChannel + padRpc.onMessageEvent.reg(function (msg) { onMessage(msg); }); + padRpc.onJoinEvent.reg(function (m) { sframeChan.event('EV_RT_JOIN', m); }); + padRpc.onLeaveEvent.reg(function (m) { sframeChan.event('EV_RT_LEAVE', m); }); }; - network.on('disconnect', function (reason) { - console.log('disconnect'); - if (isIntentionallyLeaving) { return; } - if (reason === "network.disconnect() called") { return; } + padRpc.onDisconnectEvent.reg(function () { sframeChan.event('EV_RT_DISCONNECT'); }); - network.on('reconnect', function () { - initializing = true; - connectTo(network, false); + // join the netflux network, promise to handle opening of the channel + padRpc.joinPad({ + channel: channel || null, + validateKey: validateKey, + readOnly: readOnly + }, function(data) { + onOpen(data); }); - - network.on('message', function (msg, sender) { // Direct message - var wchan = findChannelById(network.webChannels, channel); - if (wchan) { - onMessage(sender, msg, wchan, network, true); - } - }); - - connectTo(network, true); }; return { diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index c9c6c9526..da08bdc37 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -581,7 +581,7 @@ define([ CpNfOuter.start({ sframeChan: sframeChan, channel: secret.channel, - network: cfg.newNetwork || network, + padRpc: Cryptpad.padRpc, validateKey: secret.keys.validateKey || undefined, readOnly: readOnly, crypto: Crypto.createEncryptor(secret.keys), diff --git a/www/worker/inner.js b/www/worker/inner.js index 8dd1a7904..7b2186d2c 100644 --- a/www/worker/inner.js +++ b/www/worker/inner.js @@ -16,8 +16,8 @@ define([ Cryptpad, nThen, SFCommon, - UI, - Messages + UI + /*Messages*/ ) { var APP = window.APP = {}; @@ -31,7 +31,6 @@ define([ sFrameChan = common.getSframeChannel(); sFrameChan.onReady(waitFor()); }).nThen(function (/*waitFor*/) { - var $body = $('body'); var $container = $('#cp-app-worker-container'); var $bar = $('.cp-toolbar-container'); diff --git a/www/worker/worker.js b/www/worker/worker.js index 6a5babd44..095ef8980 100644 --- a/www/worker/worker.js +++ b/www/worker/worker.js @@ -1,3 +1,5 @@ +/* jshint ignore:start */ + var window = self; var localStorage = { setItem: function (k, v) { localStorage[k] = v; },