diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index 68e341562..31c65bb86 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -2271,8 +2271,9 @@ define([ localToken: tryParsing(localStorage.getItem(Constants.tokenKey)), // TODO move this to LocalStore ? language: common.getLanguage(), cache: rdyCfg.cache, + noDrive: rdyCfg.noDrive, disableCache: localStorage['CRYPTPAD_STORE|disableCache'], - driveEvents: true //rdyCfg.driveEvents // Boolean + driveEvents: !rdyCfg.noDrive //rdyCfg.driveEvents // Boolean }; common.userHash = userHash; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index d4cfb6f4f..4db6a6142 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -26,13 +26,14 @@ define([ '/bower_components/chainpad/chainpad.dist.js', '/bower_components/chainpad-netflux/chainpad-netflux.js', '/bower_components/chainpad-listmap/chainpad-listmap.js', + '/bower_components/netflux-websocket/netflux-client.js', '/bower_components/nthen/index.js', '/bower_components/saferphore/index.js', ], function (Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Pinpad, Cache, SF, Cursor, OnlyOffice, Mailbox, Profile, Team, Messenger, History, NetConfig, AppConfig, - Crypto, ChainPad, CpNetflux, Listmap, nThen, Saferphore) { + Crypto, ChainPad, CpNetflux, Listmap, Netflux, nThen, Saferphore) { var onReadyEvt = Util.mkEvent(true); var onCacheReadyEvt = Util.mkEvent(true); @@ -547,6 +548,7 @@ define([ ////////////////////////////////////////////////////////////////// var getAllStores = Store.getAllStores = function () { + if (!store.proxy) { return []; } var stores = [store]; var teamModule = store.modules['team']; if (teamModule) { @@ -568,7 +570,7 @@ define([ getColor().toString(16); }; var getUserColor = function () { - var color = Util.find(store.proxy, ['settings', 'general', 'cursor', 'color']); + var color = Util.find(store, ['proxy', 'settings', 'general', 'cursor', 'color']); if (!color) { color = getRandomColor(); Store.setAttribute(null, { @@ -581,33 +583,34 @@ define([ // Get the metadata for sframe-common-outer Store.getMetadata = function (clientId, app, cb) { - var disableThumbnails = Util.find(store.proxy, ['settings', 'general', 'disableThumbnails']); + var proxy = store.proxy || {}; + var disableThumbnails = Util.find(proxy, ['settings', 'general', 'disableThumbnails']); var teams = (store.modules['team'] && store.modules['team'].getTeamsData(app)) || {}; var metadata = { // "user" is shared with everybody via the userlist user: { - name: store.proxy[Constants.displayNameKey] || "", - uid: store.proxy.uid, - avatar: Util.find(store.proxy, ['profile', 'avatar']), - profile: Util.find(store.proxy, ['profile', 'view']), + name: proxy[Constants.displayNameKey] || store.noDriveName || "", + uid: proxy.uid || Hash.createChannelId(), // Random uid in nodrive mode + avatar: Util.find(proxy, ['profile', 'avatar']), + profile: Util.find(proxy, ['profile', 'view']), color: getUserColor(), - notifications: Util.find(store.proxy, ['mailboxes', 'notifications', 'channel']), - curvePublic: store.proxy.curvePublic, + notifications: Util.find(proxy, ['mailboxes', 'notifications', 'channel']), + curvePublic: proxy.curvePublic, }, // "priv" is not shared with other users but is needed by the apps priv: { clientId: clientId, - edPublic: store.proxy.edPublic, - friends: store.proxy.friends || {}, - settings: store.proxy.settings, + edPublic: proxy.edPublic, + friends: proxy.friends || {}, + settings: proxy.settings || NEW_USER_SETTINGS, thumbnails: disableThumbnails === false, isDriveOwned: Boolean(Util.find(store, ['driveMetadata', 'owners'])), - support: Util.find(store.proxy, ['mailboxes', 'support', 'channel']), + support: Util.find(proxy, ['mailboxes', 'support', 'channel']), driveChannel: store.driveChannel, - pendingFriends: store.proxy.friends_pending || {}, - supportPrivateKey: Util.find(store.proxy, ['mailboxes', 'supportadmin', 'keys', 'curvePrivate']), - accountName: store.proxy.login_name || '', - offline: store.offline, + pendingFriends: proxy.friends_pending || {}, + supportPrivateKey: Util.find(proxy, ['mailboxes', 'supportadmin', 'keys', 'curvePrivate']), + accountName: proxy.login_name || '', + offline: store.proxy && store.offline, teams: teams, plan: account.plan } @@ -853,6 +856,10 @@ define([ // Set the display name (username) in the proxy Store.setDisplayName = function (clientId, value, cb) { + if (!store.proxy) { + store.noDriveName = value; + return void cb(); + } if (store.modules['profile']) { store.modules['profile'].setName(value); } @@ -1444,6 +1451,8 @@ define([ }; // Teams support offline/cache mode if (obj.type === "team") { return void todo(); } + // If we're in "noDrive" mode + if (!store.proxy) { return void todo(); } // Other modules should wait for the ready event onReadyEvt.reg(todo); } @@ -1713,6 +1722,10 @@ define([ if (padData && padData.validateKey && store.messenger) { store.messenger.storeValidateKey(data.channel, padData.validateKey); } + if (!store.proxy) { + postMessage(clientId, "PAD_READY", pad.noCache); + return; + } onReadyEvt.reg(function () { postMessage(clientId, "PAD_READY", pad.noCache); }); @@ -1768,6 +1781,7 @@ define([ channel: data.channel, metadata: data.metadata, network: store.network || store.networkPromise, + websocketURL: NetConfig.getWebsocketURL(), //readOnly: data.readOnly, onConnect: function (wc, sendMessage) { channel.sendMessage = function (msg, cId, cb) { @@ -2759,6 +2773,7 @@ define([ var listmapConfig = { data: {}, websocketURL: NetConfig.getWebsocketURL(), + network: store.network, channel: secret.channel, readOnly: false, validateKey: secret.keys.validateKey || undefined, @@ -2914,6 +2929,50 @@ define([ */ var initialized = false; + // If we load CryptPad for the first time from an existing pad, don't create a + // drive automatically. + var onNoDrive = function (clientId, cb) { + var andThen = function () { + // To be able to use all the features inside the pad, we need to + // initialize the chat (messenger) and the cursor modules. + loadUniversal(Cursor, 'cursor', function () {}); + loadUniversal(Messenger, 'messenger', function () {}); + store.messenger = store.modules['messenger']; + + // And now we're ready + initAnonRpc(null, null, function () { + cb({}); + }); + }; + + // We need an anonymous RPC to be able to check if the pad exists and to get + // its metadata, so we have to create a network first. + if (!store.network) { + var wsUrl = NetConfig.getWebsocketURL(); + return void Netflux.connect(wsUrl).then(function (network) { + store.network = network; + // We need to know the HistoryKeeper ID to initialize the anon RPC + // Join a basic ephemeral channel, get the ID and leave it instantly + network.join('0000000000000000000000000000000000').then(function (wc) { + var hk; + wc.members.forEach(function (p) { if (p.length === 16) { hk = p; } }); + network.historyKeeper = hk; + wc.leave(); + + andThen(); + }, function (err) { + console.error(err); + cb({error: 'GET_HK'}); + }); + }, function (err) { + console.error(err); + cb({error: 'OFFLINE'}); + }); + } + andThen(); + }; + + Store.init = function (clientId, data, _callback) { var callback = Util.once(_callback); @@ -2948,13 +3007,32 @@ define([ Cache.disable(); } + if (data.query && data.broadcast) { + postMessage = function (clientId, cmd, d, cb) { + data.query(clientId, cmd, d, cb); + }; + broadcast = function (excludes, cmd, d, cb) { + data.broadcast(excludes, cmd, d, cb); + }; + } + + // First tab, no user hash, no anon hash and this app doesn't need a drive + // ==> don't create a drive + if (data.noDrive && !data.userHash && !data.anonHash) { + return void onNoDrive(clientId, function (obj) { + if (obj && obj.error) { + // if we can't properly initialize the noDrive mode, use normal mode + if (obj.error === 'GET_HK') { + data.noDrive = false; + Store.init(clientId, data, _callback); + return; + } + } + callback(obj); + }); + } + initialized = true; - postMessage = function (clientId, cmd, d, cb) { - data.query(clientId, cmd, d, cb); - }; - broadcast = function (excludes, cmd, d, cb) { - data.broadcast(excludes, cmd, d, cb); - }; store.data = data; connect(clientId, data, function (ret) { diff --git a/www/common/outer/cursor.js b/www/common/outer/cursor.js index 92c4378cc..b6662b75f 100644 --- a/www/common/outer/cursor.js +++ b/www/common/outer/cursor.js @@ -180,9 +180,10 @@ define([ var updateCursor = function (ctx, data, client, cb) { var c = ctx.clients[client]; if (!c) { return void cb({error: 'NO_CLIENT'}); } - data.color = Util.find(ctx.store.proxy, ['settings', 'general', 'cursor', 'color']); - data.name = ctx.store.proxy[Constants.displayNameKey] || Messages.anonymous; - data.avatar = Util.find(ctx.store.proxy, ['profile', 'avatar']); + var proxy = ctx.store.proxy || {}; + data.color = Util.find(proxy, ['settings', 'general', 'cursor', 'color']); + data.name = proxy[Constants.displayNameKey] || ctx.store.noDriveName || Messages.anonymous; + data.avatar = Util.find(proxy, ['profile', 'avatar']); c.cursor = data; sendMyCursor(ctx, client); cb(); @@ -241,6 +242,12 @@ define([ Cursor.init = function (cfg, waitFor, emit) { var cursor = {}; + + // Already initialized by a "noDrive" tab? + if (cfg.store && cfg.store.modules && cfg.store.modules['cursor']) { + return cfg.store.modules['cursor']; + } + var ctx = { store: cfg.store, emit: emit, diff --git a/www/common/outer/messenger.js b/www/common/outer/messenger.js index 301218aa1..1ac7a6e17 100644 --- a/www/common/outer/messenger.js +++ b/www/common/outer/messenger.js @@ -213,7 +213,7 @@ define([ if (parsed[0] !== Types.mapId) { return; } // Don't send your key if it's already an ACK // Answer with your own key - var proxy = ctx.store.proxy; + var proxy = ctx.store.proxy || {}; var myData = createData(proxy); delete myData.channel; var rMsg = [Types.mapIdAck, myData, channel.wc.myID]; @@ -662,7 +662,7 @@ define([ if (channel.onUserlistUpdate) { channel.onUserlistUpdate(); } - var proxy = ctx.store.proxy; + var proxy = ctx.store.proxy || {}; var online = channel.wc.members.some(function (nId) { if (nId === ctx.store.network.historyKeeper) { return; } var data = channel.mapId[nId] || undefined; @@ -673,7 +673,7 @@ define([ }; var getMyInfo = function (ctx, cb) { - var proxy = ctx.store.proxy; + var proxy = ctx.store.proxy || {}; cb({ curvePublic: proxy.curvePublic, displayName: proxy[Constants.displayNameKey] @@ -948,6 +948,14 @@ define([ Msg.init = function (cfg, waitFor, emit) { var messenger = {}; var store = cfg.store; + + // Already initialized by a "noDrive" tab + // If this one is a normal tab, add the missing listener if we can + if (store.messenger) { + store.messenger.addListener(); + return store.messenger; + } + if (AppConfig.availablePadTypes.indexOf('contacts') === -1) { return; } var ctx = { store: store, @@ -961,9 +969,14 @@ define([ range_requests: {} }; - store.proxy.on('change', ['mutedUsers'], function () { - ctx.emit('UPDATE_MUTED', null, getAllClients(ctx)); - }); + messenger.addListener = function () { + if (store.proxy) { + store.proxy.on('change', ['mutedUsers'], function () { + ctx.emit('UPDATE_MUTED', null, getAllClients(ctx)); + }); + } + }; + messenger.addListener(); ctx.store.network.on('message', function(msg, sender) { onDirectMessage(ctx, msg, sender); diff --git a/www/common/sframe-app-outer.js b/www/common/sframe-app-outer.js index 2ff61145f..3d3cfaf28 100644 --- a/www/common/sframe-app-outer.js +++ b/www/common/sframe-app-outer.js @@ -16,6 +16,7 @@ define([ }).nThen(function (/*waitFor*/) { SFCommonO.start({ cache: true, + noDrive: true, hash: hash, href: href, useCreationScreen: true, diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index d511d32df..df49ee6a3 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -222,6 +222,7 @@ define([ } catch (e) { console.error(e); } Cryptpad.ready(waitFor(), { + noDrive: cfg.noDrive, driveEvents: cfg.driveEvents, cache: Boolean(cfg.cache), currentPad: currentPad