diff --git a/customize.dist/src/outer.css b/customize.dist/src/outer.css index b36fb2c10..75f449e61 100644 --- a/customize.dist/src/outer.css +++ b/customize.dist/src/outer.css @@ -2,7 +2,7 @@ html, body { margin: 0px; padding: 0px; } -#sbox-iframe, #sbox-share-iframe, #sbox-filePicker-iframe { +#sbox-iframe, #sbox-secure-iframe { position: fixed; top:0; left:0; bottom:0; right:0; diff --git a/www/code/inner.js b/www/code/inner.js index 5b517c74c..28b90b07c 100644 --- a/www/code/inner.js +++ b/www/code/inner.js @@ -278,6 +278,7 @@ define([ if (MEDIA_TAG_MODES.indexOf(mode) !== -1) { // Embedding is endabled framework.setMediaTagEmbedder(function (mt) { + editor.focus(); editor.replaceSelection($(mt)[0].outerHTML); }); } else { diff --git a/www/common/common-interface.js b/www/common/common-interface.js index 4c4518434..b2cd042a8 100644 --- a/www/common/common-interface.js +++ b/www/common/common-interface.js @@ -196,6 +196,7 @@ define([ ]); var $frame = $(frame); frame.closeModal = function (cb) { + frame.closeModal = function () {}; // Prevent further calls $frame.fadeOut(150, function () { $frame.detach(); if (typeof(cb) === "function") { cb(); } @@ -464,16 +465,23 @@ define([ UI.createModal = function (cfg) { var $body = cfg.$body || $('body'); - var $blockContainer = $body.find('#'+cfg.id); - if (!$blockContainer.length) { - $blockContainer = $(h('div.cp-modal-container#'+cfg.id, { + var $blockContainer = cfg.id && $body.find('#'+cfg.id); + if (!$blockContainer || !$blockContainer.length) { + var id = ''; + if (cfg.id) { id = '#'+cfg.id; } + $blockContainer = $(h('div.cp-modal-container'+id, { tabindex: 1 })); } + var deleted = false; var hide = function () { - if (cfg.onClose) { return void cfg.onClose(); } + if (deleted) { return; } $blockContainer.hide(); - if (cfg.onClosed) { cfg.onClosed(); } + if (!cfg.id) { + deleted = true; + $blockContainer.remove(); + } + if (cfg.onClose) { cfg.onClose(); } }; $blockContainer.html('').appendTo($body); var $block = $(h('div.cp-modal')).appendTo($blockContainer); diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index b7c6310f5..75ee78c15 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -1606,11 +1606,7 @@ define([ .text(Messages.accessButton)) .click(common.prepareFeedback(type)) .click(function () { - require(['/common/inner/access.js'], function (Access) { - Access.getAccessModal(common, {}, function (e) { - if (e) { console.error(e); } - }); - }); + sframeChan.event('EV_ACCESS_OPEN'); }); break; case 'properties': @@ -1625,11 +1621,7 @@ define([ if (!data) { return void UI.alert(Messages.autostore_notAvailable); } - require(['/common/inner/properties.js'], function (Properties) { - Properties.getPropertiesModal(common, {}, function (e) { - if (e) { console.error(e); } - }); - }); + sframeChan.event('EV_PROPERTIES_OPEN'); }); }); break; @@ -2642,17 +2634,13 @@ define([ }); }; - UIElements.initFilePicker = function (common, cfg) { - var onSelect = cfg.onSelect || $.noop; + UIElements.openFilePicker = function (common, types, cb) { var sframeChan = common.getSframeChannel(); - sframeChan.on("EV_FILE_PICKED", function (data) { - onSelect(data); + sframeChan.query("Q_FILE_PICKER_OPEN", types, function (err, data) { + if (err) { return; } + cb(data); }); }; - UIElements.openFilePicker = function (common, types) { - var sframeChan = common.getSframeChannel(); - sframeChan.event("EV_FILE_PICKER_OPEN", types); - }; UIElements.openTemplatePicker = function (common, force) { var metadataMgr = common.getMetadataMgr(); @@ -2675,29 +2663,25 @@ define([ return; } delete pickerCfg.hidden; - common.openFilePicker(pickerCfg); var first = true; // We can only pick a template once (for a new document) - var fileDialogCfg = { - onSelect: function (data) { - if (data.type === type && first) { - UI.addLoadingScreen({hideTips: true}); - var chatChan = common.getPadChat(); - var cursorChan = common.getCursorChannel(); - sframeChan.query('Q_TEMPLATE_USE', { - href: data.href, - chat: chatChan, - cursor: cursorChan - }, function () { - first = false; - UI.removeLoadingScreen(); - Feedback.send('TEMPLATE_USED'); - }); - if (focus) { focus.focus(); } - return; - } + common.openFilePicker(pickerCfg, function (data) { + if (data.type === type && first) { + UI.addLoadingScreen({hideTips: true}); + var chatChan = common.getPadChat(); + var cursorChan = common.getCursorChannel(); + sframeChan.query('Q_TEMPLATE_USE', { + href: data.href, + chat: chatChan, + cursor: cursorChan + }, function () { + first = false; + UI.removeLoadingScreen(); + Feedback.send('TEMPLATE_USED'); + }); + if (focus) { focus.focus(); } + return; } - }; - common.initFilePicker(fileDialogCfg); + }); }; sframeChan.query("Q_TEMPLATE_EXIST", type, function (err, data) { diff --git a/www/common/inner/common-modal.js b/www/common/inner/common-modal.js index ee475c028..33ac14d9f 100644 --- a/www/common/inner/common-modal.js +++ b/www/common/inner/common-modal.js @@ -59,7 +59,7 @@ define([ }), opts.href); // If this is a file, don't try to look for metadata - if (opts.channel && opts.channel.length > 34) { return; } + if (opts.channel && opts.channel.length > 32) { return; } if (opts.channel) { data.channel = opts.channel; } Modal.loadMetadata(Env, data, waitFor); }).nThen(function () { @@ -129,7 +129,10 @@ define([ tabs[i] = { content: c && UI.dialog.customModal(node, { buttons: obj.buttons || button, - onClose: function () { blocked = false; } + onClose: function () { + blocked = false; + if (typeof(opts.onClose) === "function") { opts.onClose(); } + } }), disabled: !c, title: obj.title, diff --git a/www/common/onlyoffice/inner.js b/www/common/onlyoffice/inner.js index 3a824214c..20125d82c 100644 --- a/www/common/onlyoffice/inner.js +++ b/www/common/onlyoffice/inner.js @@ -132,6 +132,12 @@ define([ APP.onLocal(); }; + var isRegisteredUserOnline = function () { + var users = metadataMgr.getMetadata().users || {}; + return Object.keys(users).some(function (id) { + return users[id] && users[id].curvePublic; + }); + }; var isUserOnline = function (ooid) { // Remove ids for users that have left the channel deleteOffline(); @@ -348,16 +354,44 @@ define([ fixSheets(); APP.FM.handleFile(blob, data); }; + + Messages.oo_login = 'Log in...'; // XXX + var noLogin = false; + var makeCheckpoint = function (force) { - if (!common.isLoggedIn()) { return; } var locked = content.saveLock; var lastCp = getLastCp(); var needCp = force || ooChannel.cpIndex % CHECKPOINT_INTERVAL === 0 || - (ooChannel.cpIndex - lastCp.index) > CHECKPOINT_INTERVAL; + (ooChannel.cpIndex - (lastCp.index || 0)) > CHECKPOINT_INTERVAL; if (!needCp) { return; } if (!locked || !isUserOnline(locked) || force) { + if (!common.isLoggedIn() && !isRegisteredUserOnline() && !noLogin) { + var login = h('button.cp-corner-primary', Messages.login_login); + var register = h('button.cp-corner-primary', Messages.login_register); + var cancel = h('button.cp-corner-cancel', Messages.cancel); + var actions = h('div', [cancel, register, login]); + var modal = UI.cornerPopup(Messages.oo_login, actions, '', {alt: true}); + $(register).click(function () { + common.setLoginRedirect(function () { + common.gotoURL('/register/'); + }); + modal.delete(); + }); + $(login).click(function () { + common.setLoginRedirect(function () { + common.gotoURL('/login/'); + }); + modal.delete(); + }); + $(cancel).click(function () { + modal.delete(); + noLogin = true; + }); + return; + } + if (!common.isLoggedIn()) { return; } content.saveLock = myOOId; APP.onLocal(); APP.realtime.onSettle(function () { @@ -907,8 +941,16 @@ define([ if (ifr) { ifr.remove(); } }; - common.initFilePicker({ - onSelect: function (data) { + APP.AddImage = function(cb1, cb2) { + APP.AddImageSuccessCallback = cb1; + APP.AddImageErrorCallback = cb2; + common.openFilePicker({ + types: ['file'], + where: ['root'], + filter: { + fileType: ['image/'] + } + }, function (data) { if (data.type !== 'file') { debug("Unexpected data type picked " + data.type); return; @@ -929,19 +971,6 @@ define([ }); }); }); - } - }); - - APP.AddImage = function(cb1, cb2) { - APP.AddImageSuccessCallback = cb1; - APP.AddImageErrorCallback = cb2; - common.openFilePicker({ - types: ['file'], - where: ['root'], - filter: { - fileType: ['image/'] - } - }); }; diff --git a/www/common/outer/async-store.js b/www/common/outer/async-store.js index da7f043f3..1e41b8e5b 100644 --- a/www/common/outer/async-store.js +++ b/www/common/outer/async-store.js @@ -675,6 +675,8 @@ define([ sem.take(function (give) { var otherOwners = false; nThen(function (_w) { + // Don't check server metadata for blobs + if (c.length !== 32) { return; } Store.anonRpcMsg(null, { msg: 'GET_METADATA', data: c @@ -1807,6 +1809,7 @@ define([ var cb = Util.once(Util.mkAsync(_cb)); if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); } + if (data.channel.length !== 32) { return void cb({ error: 'EINVAL'}); } store.anon_rpc.send('GET_METADATA', data.channel, function (err, obj) { if (err) { return void cb({error: err}); } var metadata = (obj && obj[0]) || {}; diff --git a/www/common/outer/team.js b/www/common/outer/team.js index a2dbf7c05..c151de004 100644 --- a/www/common/outer/team.js +++ b/www/common/outer/team.js @@ -678,6 +678,8 @@ define([ sem.take(function (give) { var otherOwners = false; nThen(function (_w) { + // Don't check server metadata for blobs + if (c.length !== 32) { return; } ctx.Store.anonRpcMsg(null, { msg: 'GET_METADATA', data: c diff --git a/www/common/sframe-app-framework.js b/www/common/sframe-app-framework.js index 171c58b0e..fc149b2cc 100644 --- a/www/common/sframe-app-framework.js +++ b/www/common/sframe-app-framework.js @@ -503,8 +503,13 @@ define([ var createFilePicker = function () { if (!common.isLoggedIn()) { return; } - common.initFilePicker({ - onSelect: function (data) { + $embedButton = common.createButton('mediatag', true).click(function () { + var cfg = { + types: ['file'], + where: ['root'] + }; + if ($embedButton.data('filter')) { cfg.filter = $embedButton.data('filter'); } + common.openFilePicker(cfg, function (data) { if (data.type !== 'file') { console.log("Unexpected data type picked " + data.type); return; @@ -516,17 +521,7 @@ define([ var src = data.src = data.src.slice(0,1) === '/' ? origin + data.src : data.src; mediaTagEmbedder($(''), data); - } - }); - $embedButton = common.createButton('mediatag', true).click(function () { - var cfg = { - types: ['file'], - where: ['root'] - }; - if ($embedButton.data('filter')) { - cfg.filter = $embedButton.data('filter'); - } - common.openFilePicker(cfg); + }); }).appendTo(toolbar.$rightside).hide(); }; var setMediaTagEmbedder = function (mte, filter) { diff --git a/www/common/sframe-common-outer.js b/www/common/sframe-common-outer.js index f4f42707a..2ae6f789a 100644 --- a/www/common/sframe-common-outer.js +++ b/www/common/sframe-common-outer.js @@ -18,8 +18,7 @@ define([ var Cryptget; var SFrameChannel; var sframeChan; - var FilePicker; - var Share; + var SecureIframe; var Messaging; var Notifier; var Utils = { @@ -44,8 +43,7 @@ define([ '/bower_components/chainpad-crypto/crypto.js', '/common/cryptget.js', '/common/outer/worker-channel.js', - '/filepicker/main.js', - '/share/main.js', + '/secureiframe/main.js', '/common/common-messaging.js', '/common/common-notifier.js', '/common/common-hash.js', @@ -58,15 +56,14 @@ define([ '/common/test.js', '/common/userObject.js', ], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel, - _FilePicker, _Share, _Messaging, _Notifier, _Hash, _Util, _Realtime, + _SecureIframe, _Messaging, _Notifier, _Hash, _Util, _Realtime, _Constants, _Feedback, _LocalStore, _AppConfig, _Test, _UserObject) { CpNfOuter = _CpNfOuter; Cryptpad = _Cryptpad; Crypto = Utils.Crypto = _Crypto; Cryptget = _Cryptget; SFrameChannel = _SFrameChannel; - FilePicker = _FilePicker; - Share = _Share; + SecureIframe = _SecureIframe; Messaging = _Messaging; Notifier = _Notifier; Utils.Hash = _Hash; @@ -491,7 +488,7 @@ define([ // Put in the following function the RPC queries that should also work in filepicker - var addCommonRpc = function (sframeChan) { + var addCommonRpc = function (sframeChan, safe) { sframeChan.on('Q_ANON_RPC_MESSAGE', function (data, cb) { Cryptpad.anonRpcMsg(data.msg, data.content, function (err, response) { cb({error: err, response: response}); @@ -598,6 +595,12 @@ define([ } if (data.href) { href = data.href; } Cryptpad.getPadAttribute(data.key, function (e, data) { + if (!safe && data) { + // Remove unsafe data for the unsafe iframe + delete data.href; + delete data.roHref; + delete data.password; + } cb({ error: e, data: data @@ -976,81 +979,63 @@ define([ onFileUpload(sframeChan, data, cb); }); - // File picker - var FP = {}; - var initFilePicker = function (cfg) { - // cfg.hidden means pre-loading the filepicker while keeping it hidden. + // Secure modal + var SecureModal = {}; + // Create or display the iframe and modal + var initSecureModal = function (type, cfg, cb) { + cfg.modal = type; + SecureModal.cb = cb; + // cfg.hidden means pre-loading the iframe while keeping it hidden. // if cfg.hidden is true and the iframe already exists, do nothing - if (!FP.$iframe) { + if (!SecureModal.$iframe) { var config = {}; - config.onFilePicked = function (data) { - sframeChan.event('EV_FILE_PICKED', data); + config.onAction = function (data) { + if (typeof(SecureModal.cb) !== "function") { return; } + SecureModal.cb(data); + SecureModal.$iframe.hide(); }; config.onClose = function () { - FP.$iframe.hide(); - }; - config.onFileUpload = onFileUpload; - config.types = cfg; - config.addCommonRpc = addCommonRpc; - config.modules = { - Cryptpad: Cryptpad, - SFrameChannel: SFrameChannel, - Utils: Utils + SecureModal.$iframe.hide(); }; - FP.$iframe = $('