Merge branch 'q' into staging

pull/1/head
yflory 5 years ago
commit cdcb01623d

@ -2,7 +2,7 @@ html, body {
margin: 0px; margin: 0px;
padding: 0px; padding: 0px;
} }
#sbox-iframe, #sbox-share-iframe, #sbox-filePicker-iframe { #sbox-iframe, #sbox-secure-iframe {
position: fixed; position: fixed;
top:0; left:0; top:0; left:0;
bottom:0; right:0; bottom:0; right:0;

@ -278,6 +278,7 @@ define([
if (MEDIA_TAG_MODES.indexOf(mode) !== -1) { if (MEDIA_TAG_MODES.indexOf(mode) !== -1) {
// Embedding is endabled // Embedding is endabled
framework.setMediaTagEmbedder(function (mt) { framework.setMediaTagEmbedder(function (mt) {
editor.focus();
editor.replaceSelection($(mt)[0].outerHTML); editor.replaceSelection($(mt)[0].outerHTML);
}); });
} else { } else {

@ -196,6 +196,7 @@ define([
]); ]);
var $frame = $(frame); var $frame = $(frame);
frame.closeModal = function (cb) { frame.closeModal = function (cb) {
frame.closeModal = function () {}; // Prevent further calls
$frame.fadeOut(150, function () { $frame.fadeOut(150, function () {
$frame.detach(); $frame.detach();
if (typeof(cb) === "function") { cb(); } if (typeof(cb) === "function") { cb(); }
@ -464,16 +465,23 @@ define([
UI.createModal = function (cfg) { UI.createModal = function (cfg) {
var $body = cfg.$body || $('body'); var $body = cfg.$body || $('body');
var $blockContainer = $body.find('#'+cfg.id); var $blockContainer = cfg.id && $body.find('#'+cfg.id);
if (!$blockContainer.length) { if (!$blockContainer || !$blockContainer.length) {
$blockContainer = $(h('div.cp-modal-container#'+cfg.id, { var id = '';
if (cfg.id) { id = '#'+cfg.id; }
$blockContainer = $(h('div.cp-modal-container'+id, {
tabindex: 1 tabindex: 1
})); }));
} }
var deleted = false;
var hide = function () { var hide = function () {
if (cfg.onClose) { return void cfg.onClose(); } if (deleted) { return; }
$blockContainer.hide(); $blockContainer.hide();
if (cfg.onClosed) { cfg.onClosed(); } if (!cfg.id) {
deleted = true;
$blockContainer.remove();
}
if (cfg.onClose) { cfg.onClose(); }
}; };
$blockContainer.html('').appendTo($body); $blockContainer.html('').appendTo($body);
var $block = $(h('div.cp-modal')).appendTo($blockContainer); var $block = $(h('div.cp-modal')).appendTo($blockContainer);

@ -1606,11 +1606,7 @@ define([
.text(Messages.accessButton)) .text(Messages.accessButton))
.click(common.prepareFeedback(type)) .click(common.prepareFeedback(type))
.click(function () { .click(function () {
require(['/common/inner/access.js'], function (Access) { sframeChan.event('EV_ACCESS_OPEN');
Access.getAccessModal(common, {}, function (e) {
if (e) { console.error(e); }
});
});
}); });
break; break;
case 'properties': case 'properties':
@ -1625,11 +1621,7 @@ define([
if (!data) { if (!data) {
return void UI.alert(Messages.autostore_notAvailable); return void UI.alert(Messages.autostore_notAvailable);
} }
require(['/common/inner/properties.js'], function (Properties) { sframeChan.event('EV_PROPERTIES_OPEN');
Properties.getPropertiesModal(common, {}, function (e) {
if (e) { console.error(e); }
});
});
}); });
}); });
break; break;
@ -2642,17 +2634,13 @@ define([
}); });
}; };
UIElements.initFilePicker = function (common, cfg) { UIElements.openFilePicker = function (common, types, cb) {
var onSelect = cfg.onSelect || $.noop;
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
sframeChan.on("EV_FILE_PICKED", function (data) { sframeChan.query("Q_FILE_PICKER_OPEN", types, function (err, data) {
onSelect(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) { UIElements.openTemplatePicker = function (common, force) {
var metadataMgr = common.getMetadataMgr(); var metadataMgr = common.getMetadataMgr();
@ -2675,10 +2663,8 @@ define([
return; return;
} }
delete pickerCfg.hidden; delete pickerCfg.hidden;
common.openFilePicker(pickerCfg);
var first = true; // We can only pick a template once (for a new document) var first = true; // We can only pick a template once (for a new document)
var fileDialogCfg = { common.openFilePicker(pickerCfg, function (data) {
onSelect: function (data) {
if (data.type === type && first) { if (data.type === type && first) {
UI.addLoadingScreen({hideTips: true}); UI.addLoadingScreen({hideTips: true});
var chatChan = common.getPadChat(); var chatChan = common.getPadChat();
@ -2695,9 +2681,7 @@ define([
if (focus) { focus.focus(); } if (focus) { focus.focus(); }
return; return;
} }
} });
};
common.initFilePicker(fileDialogCfg);
}; };
sframeChan.query("Q_TEMPLATE_EXIST", type, function (err, data) { sframeChan.query("Q_TEMPLATE_EXIST", type, function (err, data) {

@ -59,7 +59,7 @@ define([
}), opts.href); }), opts.href);
// If this is a file, don't try to look for metadata // 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; } if (opts.channel) { data.channel = opts.channel; }
Modal.loadMetadata(Env, data, waitFor); Modal.loadMetadata(Env, data, waitFor);
}).nThen(function () { }).nThen(function () {
@ -129,7 +129,10 @@ define([
tabs[i] = { tabs[i] = {
content: c && UI.dialog.customModal(node, { content: c && UI.dialog.customModal(node, {
buttons: obj.buttons || button, buttons: obj.buttons || button,
onClose: function () { blocked = false; } onClose: function () {
blocked = false;
if (typeof(opts.onClose) === "function") { opts.onClose(); }
}
}), }),
disabled: !c, disabled: !c,
title: obj.title, title: obj.title,

@ -132,6 +132,12 @@ define([
APP.onLocal(); 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) { var isUserOnline = function (ooid) {
// Remove ids for users that have left the channel // Remove ids for users that have left the channel
deleteOffline(); deleteOffline();
@ -348,16 +354,44 @@ define([
fixSheets(); fixSheets();
APP.FM.handleFile(blob, data); APP.FM.handleFile(blob, data);
}; };
Messages.oo_login = 'Log in...'; // XXX
var noLogin = false;
var makeCheckpoint = function (force) { var makeCheckpoint = function (force) {
if (!common.isLoggedIn()) { return; }
var locked = content.saveLock; var locked = content.saveLock;
var lastCp = getLastCp(); var lastCp = getLastCp();
var needCp = force || ooChannel.cpIndex % CHECKPOINT_INTERVAL === 0 || 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 (!needCp) { return; }
if (!locked || !isUserOnline(locked) || force) { 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; content.saveLock = myOOId;
APP.onLocal(); APP.onLocal();
APP.realtime.onSettle(function () { APP.realtime.onSettle(function () {
@ -907,8 +941,16 @@ define([
if (ifr) { ifr.remove(); } if (ifr) { ifr.remove(); }
}; };
common.initFilePicker({ APP.AddImage = function(cb1, cb2) {
onSelect: function (data) { APP.AddImageSuccessCallback = cb1;
APP.AddImageErrorCallback = cb2;
common.openFilePicker({
types: ['file'],
where: ['root'],
filter: {
fileType: ['image/']
}
}, function (data) {
if (data.type !== 'file') { if (data.type !== 'file') {
debug("Unexpected data type picked " + data.type); debug("Unexpected data type picked " + data.type);
return; 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/']
}
}); });
}; };

@ -675,6 +675,8 @@ define([
sem.take(function (give) { sem.take(function (give) {
var otherOwners = false; var otherOwners = false;
nThen(function (_w) { nThen(function (_w) {
// Don't check server metadata for blobs
if (c.length !== 32) { return; }
Store.anonRpcMsg(null, { Store.anonRpcMsg(null, {
msg: 'GET_METADATA', msg: 'GET_METADATA',
data: c data: c
@ -1807,6 +1809,7 @@ define([
var cb = Util.once(Util.mkAsync(_cb)); var cb = Util.once(Util.mkAsync(_cb));
if (!data.channel) { return void cb({ error: 'ENOTFOUND'}); } 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) { store.anon_rpc.send('GET_METADATA', data.channel, function (err, obj) {
if (err) { return void cb({error: err}); } if (err) { return void cb({error: err}); }
var metadata = (obj && obj[0]) || {}; var metadata = (obj && obj[0]) || {};

@ -678,6 +678,8 @@ define([
sem.take(function (give) { sem.take(function (give) {
var otherOwners = false; var otherOwners = false;
nThen(function (_w) { nThen(function (_w) {
// Don't check server metadata for blobs
if (c.length !== 32) { return; }
ctx.Store.anonRpcMsg(null, { ctx.Store.anonRpcMsg(null, {
msg: 'GET_METADATA', msg: 'GET_METADATA',
data: c data: c

@ -503,8 +503,13 @@ define([
var createFilePicker = function () { var createFilePicker = function () {
if (!common.isLoggedIn()) { return; } if (!common.isLoggedIn()) { return; }
common.initFilePicker({ $embedButton = common.createButton('mediatag', true).click(function () {
onSelect: function (data) { var cfg = {
types: ['file'],
where: ['root']
};
if ($embedButton.data('filter')) { cfg.filter = $embedButton.data('filter'); }
common.openFilePicker(cfg, function (data) {
if (data.type !== 'file') { if (data.type !== 'file') {
console.log("Unexpected data type picked " + data.type); console.log("Unexpected data type picked " + data.type);
return; return;
@ -516,17 +521,7 @@ define([
var src = data.src = data.src.slice(0,1) === '/' ? origin + data.src : data.src; var src = data.src = data.src.slice(0,1) === '/' ? origin + data.src : data.src;
mediaTagEmbedder($('<media-tag src="' + src + mediaTagEmbedder($('<media-tag src="' + src +
'" data-crypto-key="cryptpad:' + data.key + '"></media-tag>'), data); '" data-crypto-key="cryptpad:' + data.key + '"></media-tag>'), 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(); }).appendTo(toolbar.$rightside).hide();
}; };
var setMediaTagEmbedder = function (mte, filter) { var setMediaTagEmbedder = function (mte, filter) {

@ -18,8 +18,7 @@ define([
var Cryptget; var Cryptget;
var SFrameChannel; var SFrameChannel;
var sframeChan; var sframeChan;
var FilePicker; var SecureIframe;
var Share;
var Messaging; var Messaging;
var Notifier; var Notifier;
var Utils = { var Utils = {
@ -44,8 +43,7 @@ define([
'/bower_components/chainpad-crypto/crypto.js', '/bower_components/chainpad-crypto/crypto.js',
'/common/cryptget.js', '/common/cryptget.js',
'/common/outer/worker-channel.js', '/common/outer/worker-channel.js',
'/filepicker/main.js', '/secureiframe/main.js',
'/share/main.js',
'/common/common-messaging.js', '/common/common-messaging.js',
'/common/common-notifier.js', '/common/common-notifier.js',
'/common/common-hash.js', '/common/common-hash.js',
@ -58,15 +56,14 @@ define([
'/common/test.js', '/common/test.js',
'/common/userObject.js', '/common/userObject.js',
], waitFor(function (_CpNfOuter, _Cryptpad, _Crypto, _Cryptget, _SFrameChannel, ], 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) { _Constants, _Feedback, _LocalStore, _AppConfig, _Test, _UserObject) {
CpNfOuter = _CpNfOuter; CpNfOuter = _CpNfOuter;
Cryptpad = _Cryptpad; Cryptpad = _Cryptpad;
Crypto = Utils.Crypto = _Crypto; Crypto = Utils.Crypto = _Crypto;
Cryptget = _Cryptget; Cryptget = _Cryptget;
SFrameChannel = _SFrameChannel; SFrameChannel = _SFrameChannel;
FilePicker = _FilePicker; SecureIframe = _SecureIframe;
Share = _Share;
Messaging = _Messaging; Messaging = _Messaging;
Notifier = _Notifier; Notifier = _Notifier;
Utils.Hash = _Hash; Utils.Hash = _Hash;
@ -491,7 +488,7 @@ define([
// Put in the following function the RPC queries that should also work in filepicker // 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) { sframeChan.on('Q_ANON_RPC_MESSAGE', function (data, cb) {
Cryptpad.anonRpcMsg(data.msg, data.content, function (err, response) { Cryptpad.anonRpcMsg(data.msg, data.content, function (err, response) {
cb({error: err, response: response}); cb({error: err, response: response});
@ -598,6 +595,12 @@ define([
} }
if (data.href) { href = data.href; } if (data.href) { href = data.href; }
Cryptpad.getPadAttribute(data.key, function (e, data) { 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({ cb({
error: e, error: e,
data: data data: data
@ -976,81 +979,63 @@ define([
onFileUpload(sframeChan, data, cb); onFileUpload(sframeChan, data, cb);
}); });
// File picker // Secure modal
var FP = {}; var SecureModal = {};
var initFilePicker = function (cfg) { // Create or display the iframe and modal
// cfg.hidden means pre-loading the filepicker while keeping it hidden. 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 cfg.hidden is true and the iframe already exists, do nothing
if (!FP.$iframe) { if (!SecureModal.$iframe) {
var config = {}; var config = {};
config.onFilePicked = function (data) { config.onAction = function (data) {
sframeChan.event('EV_FILE_PICKED', data); if (typeof(SecureModal.cb) !== "function") { return; }
SecureModal.cb(data);
SecureModal.$iframe.hide();
}; };
config.onClose = function () { config.onClose = function () {
FP.$iframe.hide(); SecureModal.$iframe.hide();
};
config.data = {
hashes: hashes,
password: password,
isTemplate: isTemplate
}; };
config.onFileUpload = onFileUpload;
config.types = cfg;
config.addCommonRpc = addCommonRpc; config.addCommonRpc = addCommonRpc;
config.modules = { config.modules = {
Cryptpad: Cryptpad, Cryptpad: Cryptpad,
SFrameChannel: SFrameChannel, SFrameChannel: SFrameChannel,
Utils: Utils Utils: Utils
}; };
FP.$iframe = $('<iframe>', {id: 'sbox-filePicker-iframe'}).appendTo($('body')); SecureModal.$iframe = $('<iframe>', {id: 'sbox-secure-iframe'}).appendTo($('body'));
FP.picker = FilePicker.create(config); SecureModal.modal = SecureIframe.create(config);
} else if (!cfg.hidden) { } else if (!cfg.hidden) {
FP.$iframe.show(); SecureModal.modal.refresh(cfg, function () {
FP.picker.refresh(cfg); SecureModal.$iframe.show();
});
} }
if (cfg.hidden) { if (cfg.hidden) {
FP.$iframe.hide(); SecureModal.$iframe.hide();
return; return;
} }
FP.$iframe.focus(); SecureModal.$iframe.focus();
}; };
sframeChan.on('EV_FILE_PICKER_OPEN', function (data) {
initFilePicker(data); sframeChan.on('Q_FILE_PICKER_OPEN', function (data, cb) {
initSecureModal('filepicker', data || {}, cb);
}); });
// Share modal sframeChan.on('EV_PROPERTIES_OPEN', function (data) {
var ShareModal = {}; initSecureModal('properties', data || {}, null);
var initShareModal = function (cfg) {
cfg.hashes = hashes;
cfg.password = password;
cfg.isTemplate = isTemplate;
// cfg.hidden means pre-loading the filepicker while keeping it hidden.
// if cfg.hidden is true and the iframe already exists, do nothing
if (!ShareModal.$iframe) {
var config = {};
config.onShareAction = function (data) {
sframeChan.event('EV_SHARE_ACTION', data);
};
config.onClose = function () {
ShareModal.$iframe.hide();
};
config.data = cfg;
config.addCommonRpc = addCommonRpc;
config.modules = {
Cryptpad: Cryptpad,
SFrameChannel: SFrameChannel,
Utils: Utils
};
ShareModal.$iframe = $('<iframe>', {id: 'sbox-share-iframe'}).appendTo($('body'));
ShareModal.modal = Share.create(config);
} else if (!cfg.hidden) {
ShareModal.modal.refresh(cfg, function () {
ShareModal.$iframe.show();
}); });
}
if (cfg.hidden) { sframeChan.on('EV_ACCESS_OPEN', function (data) {
ShareModal.$iframe.hide(); initSecureModal('access', data || {}, null);
return; });
}
ShareModal.$iframe.focus();
};
sframeChan.on('EV_SHARE_OPEN', function (data) { sframeChan.on('EV_SHARE_OPEN', function (data) {
initShareModal(data || {}); initSecureModal('share', data || {}, null);
}); });
sframeChan.on('Q_TEMPLATE_USE', function (data, cb) { sframeChan.on('Q_TEMPLATE_USE', function (data, cb) {
@ -1266,6 +1251,9 @@ define([
var _parsed = Utils.Hash.parsePadUrl(metadata.roHref); var _parsed = Utils.Hash.parsePadUrl(metadata.roHref);
_secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password); _secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password);
} }
if (_secret.channel.length !== 32) {
return void cb({error: 'EINVAL'});
}
var crypto = Crypto.createEncryptor(_secret.keys); var crypto = Crypto.createEncryptor(_secret.keys);
nThen(function (waitFor) { nThen(function (waitFor) {
// Try to get the owner's mailbox from the pad metadata first. // Try to get the owner's mailbox from the pad metadata first.
@ -1325,6 +1313,9 @@ define([
var _parsed = Utils.Hash.parsePadUrl(metadata.href || metadata.roHref); var _parsed = Utils.Hash.parsePadUrl(metadata.href || metadata.roHref);
_secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password); _secret = Utils.Hash.getSecrets(_parsed.type, _parsed.hash, metadata.password);
} }
if (_secret.channel.length !== 32) {
return void cb({error: 'EINVAL'});
}
var crypto = Crypto.createEncryptor(_secret.keys); var crypto = Crypto.createEncryptor(_secret.keys);
nThen(function (waitFor) { nThen(function (waitFor) {
// If we already have metadata, use it, otherwise, try to get it // If we already have metadata, use it, otherwise, try to get it

@ -89,7 +89,6 @@ define([
window.CryptPad_UIElements = UIElements; window.CryptPad_UIElements = UIElements;
window.CryptPad_common = funcs; window.CryptPad_common = funcs;
funcs.createUserAdminMenu = callWithCommon(UIElements.createUserAdminMenu); funcs.createUserAdminMenu = callWithCommon(UIElements.createUserAdminMenu);
funcs.initFilePicker = callWithCommon(UIElements.initFilePicker);
funcs.openFilePicker = callWithCommon(UIElements.openFilePicker); funcs.openFilePicker = callWithCommon(UIElements.openFilePicker);
funcs.openTemplatePicker = callWithCommon(UIElements.openTemplatePicker); funcs.openTemplatePicker = callWithCommon(UIElements.openTemplatePicker);
funcs.displayMediatagImage = callWithCommon(MT.displayMediatagImage); funcs.displayMediatagImage = callWithCommon(MT.displayMediatagImage);

@ -1,134 +0,0 @@
// Load #1, load as little as possible because we are in a race to get the loading screen up.
define([
'/bower_components/nthen/index.js',
'/api/config',
'jquery',
'/common/requireconfig.js',
], function (nThen, ApiConfig, $, RequireConfig) {
var requireConfig = RequireConfig();
var create = function (config) {
// Loaded in load #2
var sframeChan;
nThen(function (waitFor) {
$(waitFor());
}).nThen(function (waitFor) {
var req = {
cfg: requireConfig,
req: [ '/common/loading.js' ],
pfx: window.location.origin
};
window.rc = requireConfig;
window.apiconf = ApiConfig;
$('#sbox-filePicker-iframe').attr('src',
ApiConfig.httpSafeOrigin + '/filepicker/inner.html?' + requireConfig.urlArgs +
'#' + encodeURIComponent(JSON.stringify(req)));
// This is a cheap trick to avoid loading sframe-channel in parallel with the
// loading screen setup.
var done = waitFor();
var onMsg = function (msg) {
var data = JSON.parse(msg.data);
if (data.q !== 'READY') { return; }
window.removeEventListener('message', onMsg);
var _done = done;
done = function () { };
_done();
};
window.addEventListener('message', onMsg);
}).nThen(function (/*waitFor*/) {
var Cryptpad = config.modules.Cryptpad;
var Utils = config.modules.Utils;
nThen(function (waitFor) {
// The inner iframe tries to get some data from us every ms (cache, store...).
// It will send a "READY" message and wait for our answer with the correct txid.
// First, we have to answer to this message, otherwise we're going to block
// sframe-boot.js. Then we can start the channel.
var msgEv = Utils.Util.mkEvent();
var iframe = $('#sbox-filePicker-iframe')[0].contentWindow;
var postMsg = function (data) {
iframe.postMessage(data, '*');
};
var w = waitFor();
var whenReady = function (msg) {
if (msg.source !== iframe) { return; }
var data = JSON.parse(msg.data);
if (!data.txid) { return; }
// Remove the listener once we've received the READY message
window.removeEventListener('message', whenReady);
// Answer with the requested data
postMsg(JSON.stringify({ txid: data.txid, language: Cryptpad.getLanguage(), localStore: window.localStore, cache: window.cpCache }));
// Then start the channel
window.addEventListener('message', function (msg) {
if (msg.source !== iframe) { return; }
msgEv.fire(msg);
});
config.modules.SFrameChannel.create(msgEv, postMsg, waitFor(function (sfc) {
sframeChan = sfc;
}));
w();
};
window.addEventListener('message', whenReady);
}).nThen(function () {
var updateMeta = function () {
//console.log('EV_METADATA_UPDATE');
var metaObj;
nThen(function (waitFor) {
Cryptpad.getMetadata(waitFor(function (err, n) {
if (err) { console.log(err); }
metaObj = n;
}));
}).nThen(function (/*waitFor*/) {
metaObj.doc = {};
var additionalPriv = {
fileHost: ApiConfig.fileHost,
loggedIn: Utils.LocalStore.isLoggedIn(),
origin: window.location.origin,
pathname: window.location.pathname,
feedbackAllowed: Utils.Feedback.state,
types: config.types
};
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
sframeChan.event('EV_METADATA_UPDATE', metaObj);
});
};
Cryptpad.onMetadataChanged(updateMeta);
sframeChan.onReg('EV_METADATA_UPDATE', updateMeta);
config.addCommonRpc(sframeChan);
sframeChan.on('Q_GET_FILES_LIST', function (types, cb) {
Cryptpad.getSecureFilesList(types, function (err, data) {
cb({
error: err,
data: data
});
});
});
sframeChan.on('EV_FILE_PICKER_CLOSE', function () {
config.onClose();
});
sframeChan.on('EV_FILE_PICKED', function (data) {
config.onFilePicked(data);
});
sframeChan.on('Q_UPLOAD_FILE', function (data, cb) {
config.onFileUpload(sframeChan, data, cb);
});
});
});
var refresh = function (types) {
if (!sframeChan) { return; }
sframeChan.event('EV_FILE_PICKER_REFRESH', types);
};
return {
refresh: refresh
};
};
return {
create: create
};
});

@ -1206,22 +1206,18 @@ define([
updatePublishButton(); updatePublishButton();
if (common.isLoggedIn()) { if (common.isLoggedIn()) {
var fileDialogCfg = {
onSelect: function (data) {
if (data.type === 'file' && APP.editor) {
var mt = '<media-tag src="' + data.src + '" data-crypto-key="cryptpad:' + data.key + '"></media-tag>';
APP.editor.replaceSelection(mt);
return;
}
}
};
common.initFilePicker(fileDialogCfg);
APP.$mediaTagButton = common.createButton('mediatag', true).click(function () { APP.$mediaTagButton = common.createButton('mediatag', true).click(function () {
var pickerCfg = { var pickerCfg = {
types: ['file'], types: ['file'],
where: ['root'] where: ['root']
}; };
common.openFilePicker(pickerCfg); common.openFilePicker(pickerCfg, function (data) {
if (data.type === 'file' && APP.editor) {
var mt = '<media-tag src="' + data.src + '" data-crypto-key="cryptpad:' + data.key + '"></media-tag>';
APP.editor.replaceSelection(mt);
return;
}
});
}).appendTo($rightside); }).appendTo($rightside);
var $tags = common.createButton('hashtag', true); var $tags = common.createButton('hashtag', true);

@ -6,8 +6,11 @@
@import (reference) '../../customize/src/less2/include/tippy.less'; @import (reference) '../../customize/src/less2/include/tippy.less';
@import (reference) '../../customize/src/less2/include/checkmark.less'; @import (reference) '../../customize/src/less2/include/checkmark.less';
@import (reference) '../../customize/src/less2/include/password-input.less'; @import (reference) '../../customize/src/less2/include/password-input.less';
@import (reference) '../../customize/src/less2/include/modals-ui-elements.less';
@import (reference) '../../customize/src/less2/include/usergrid.less';
&.cp-app-filepicker { &.cp-app-secureiframe {
.modals-ui-elements_main();
.iconColors_main(); .iconColors_main();
.fileupload_main(); .fileupload_main();
.alertify_main(); .alertify_main();
@ -15,6 +18,7 @@
.checkmark_main(20px); .checkmark_main(20px);
.password_main(); .password_main();
.modal_main(); .modal_main();
.usergrid_main();
#cp-filepicker-dialog { #cp-filepicker-dialog {
display: none; display: none;
@ -26,6 +30,14 @@
overflow-y: auto; overflow-y: auto;
} }
.cp-loading-spinner-container {
height: 100px;
.cp-spinner {
border-color: @colortheme_logo-2;
border-top-color: transparent;
}
}
.cp-filepicker-content-element { .cp-filepicker-content-element {
width: 125px; width: 125px;
//min-width: 200px; //min-width: 200px;

@ -2,7 +2,7 @@
<html style="height: 100%; background: transparent;"> <html style="height: 100%; background: transparent;">
<head> <head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/> <meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script async data-bootload="/filepicker/inner.js" data-main="/common/sframe-boot.js?ver=1.6" src="/bower_components/requirejs/require.js?ver=2.3.5"></script> <script async data-bootload="/secureiframe/inner.js" data-main="/common/sframe-boot.js?ver=1.6" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<style> <style>
.loading-hidden { display: none; } .loading-hidden { display: none; }
body #cp-loading { body #cp-loading {
@ -22,7 +22,7 @@
} }
</style> </style>
</head> </head>
<body class="cp-app-filepicker" style="background: transparent;"> <body class="cp-app-secureiframe" style="background: transparent;">
</body> </body>
</html> </html>

@ -7,12 +7,13 @@ define([
'/common/common-ui-elements.js', '/common/common-ui-elements.js',
'/common/common-util.js', '/common/common-util.js',
'/common/common-hash.js', '/common/common-hash.js',
'/common/hyperscript.js',
'json.sortify', 'json.sortify',
'/customize/messages.js', '/customize/messages.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css', 'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css', 'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/filepicker/app-filepicker.less', 'less!/secureiframe/app-secure.less',
], function ( ], function (
$, $,
Crypto, Crypto,
@ -22,6 +23,7 @@ define([
UIElements, UIElements,
Util, Util,
Hash, Hash,
h,
Sortify, Sortify,
Messages) Messages)
{ {
@ -29,23 +31,88 @@ define([
var andThen = function (common) { var andThen = function (common) {
var metadataMgr = common.getMetadataMgr(); var metadataMgr = common.getMetadataMgr();
var privateData = metadataMgr.getPrivateData();
var $body = $('body');
var sframeChan = common.getSframeChannel(); var sframeChan = common.getSframeChannel();
var filters = metadataMgr.getPrivateData().types; var $body = $('body');
var hideIframe = function () {
sframeChan.event('EV_SECURE_IFRAME_CLOSE');
};
var displayed;
var create = {};
// Share modal
create['share'] = function (data) {
var priv = metadataMgr.getPrivateData();
var f = (data && data.file) ? UIElements.createFileShareModal
: UIElements.createShareModal;
var friends = common.getFriends();
var _modal;
var modal = f({
origin: priv.origin,
pathname: priv.pathname,
password: priv.password,
isTemplate: priv.isTemplate,
hashes: priv.hashes,
common: common,
title: data.title,
friends: friends,
onClose: function () {
if (_modal && _modal.close) { _modal.close(); }
hideIframe();
},
fileData: {
hash: priv.hashes.fileHash,
password: priv.password
}
});
$('button.cancel').click(); // Close any existing alertify
_modal = UI.openCustomModal(modal);
displayed = modal;
};
var hideFileDialog = function () { // Properties modal
sframeChan.event('EV_FILE_PICKER_CLOSE'); create['properties'] = function () {
require(['/common/inner/properties.js'], function (Properties) {
Properties.getPropertiesModal(common, {
onClose: function () {
hideIframe();
}
}, function (e, modal) {
if (e) { console.error(e); }
displayed = modal;
});
});
};
// Access modal
create['access'] = function () {
require(['/common/inner/access.js'], function (Access) {
Access.getAccessModal(common, {
onClose: function () {
hideIframe();
}
}, function (e, modal) {
if (e) { console.error(e); }
displayed = modal;
});
});
}; };
// File uploader
var onFilePicked = function (data) { var onFilePicked = function (data) {
var privateData = metadataMgr.getPrivateData();
var parsed = Hash.parsePadUrl(data.url); var parsed = Hash.parsePadUrl(data.url);
hideFileDialog(); if (displayed && displayed.hide) { displayed.hide(); }
hideIframe();
if (parsed.type === 'file') { if (parsed.type === 'file') {
var secret = Hash.getSecrets('file', parsed.hash, data.password); var secret = Hash.getSecrets('file', parsed.hash, data.password);
var fileHost = privateData.fileHost || privateData.origin; var fileHost = privateData.fileHost || privateData.origin;
var src = fileHost + Hash.getBlobPathFromHex(secret.channel); var src = fileHost + Hash.getBlobPathFromHex(secret.channel);
var key = Hash.encodeBase64(secret.keys.cryptKey); var key = Hash.encodeBase64(secret.keys.cryptKey);
sframeChan.event("EV_FILE_PICKED", { sframeChan.event("EV_SECURE_ACTION", {
type: parsed.type, type: parsed.type,
src: src, src: src,
name: data.name, name: data.name,
@ -53,14 +120,12 @@ define([
}); });
return; return;
} }
sframeChan.event("EV_FILE_PICKED", { sframeChan.event("EV_SECURE_ACTION", {
type: parsed.type, type: parsed.type,
href: data.url, href: data.url,
name: data.name name: data.name
}); });
}; };
// File uploader
var fmConfig = { var fmConfig = {
body: $('body'), body: $('body'),
noHandlers: true, noHandlers: true,
@ -69,40 +134,40 @@ define([
} }
}; };
APP.FM = common.createFileManager(fmConfig); APP.FM = common.createFileManager(fmConfig);
create['filepicker'] = function (_filters) {
var updateContainer = function () {};
// Create file picker var filters = _filters;
var onSelect = function (url, name, password) { var types = filters.types || [];
onFilePicked({url: url, name: name, password: password});
};
var data = { var data = {
FM: APP.FM FM: APP.FM
}; };
var updateContainer;
var createFileDialog = function () {
var types = filters.types || [];
// Create modal // Create modal
var modal = UI.createModal({ var modal = UI.createModal({
id: 'cp-filepicker-dialog',
$body: $body, $body: $body,
onClose: hideFileDialog onClose: function () {
hideIframe();
}
}); });
displayed = modal;
modal.show(); modal.show();
var $blockContainer = modal.$modal;
// Set the fixed content // Set the fixed content
var $block = $blockContainer.find('.cp-modal'); modal.$modal.attr('id', 'cp-filepicker-dialog');
var $block = modal.$modal.find('.cp-modal');
// Description // Description
var text = Messages.filePicker_description; var text = Messages.filePicker_description;
if (types && types.length === 1 && types[0] !== 'file') { if (types && types.length === 1 && types[0] !== 'file') {
// Should be Templates
text = Messages.selectTemplate; text = Messages.selectTemplate;
} }
var $description = $('<p>').text(text); $block.append(h('p', text));
$block.append($description);
var $filter = $('<p>', {'class': 'cp-modal-form'}).appendTo($block); // Add filter input
var $filter = $(h('p.cp-modal-form')).appendTo($block);
var to; var to;
$('<input>', { var $input = $('<input>', {
type: 'text', type: 'text',
'class': 'cp-filepicker-filter', 'class': 'cp-filepicker-filter',
'placeholder': Messages.filePicker_filter 'placeholder': Messages.filePicker_filter
@ -126,15 +191,16 @@ define([
$filter.append(common.createButton('upload', false, data)); $filter.append(common.createButton('upload', false, data));
} }
var $container = $('<span>', {'class': 'cp-filepicker-content'}).appendTo($block); var $container = $(h('span.cp-filepicker-content', [
h('div.cp-loading-spinner-container', h('span.cp-spinner'))
])).appendTo($block);
// Update the files list when needed // Update the files list when needed
updateContainer = function () { updateContainer = function () {
$container.html('');
var $input = $filter.find('.cp-filepicker-filter');
var filter = $input.val().trim(); var filter = $input.val().trim();
var todo = function (err, list) { var todo = function (err, list) {
if (err) { return void console.error(err); } if (err) { return void console.error(err); }
$container.html('');
Object.keys(list).forEach(function (id) { Object.keys(list).forEach(function (id) {
var data = list[id]; var data = list[id];
var name = data.filename || data.title || '?'; var name = data.filename || data.title || '?';
@ -149,8 +215,8 @@ define([
$('<span>', {'class': 'cp-filepicker-content-element-name'}).text(name) $('<span>', {'class': 'cp-filepicker-content-element-name'}).text(name)
.appendTo($span); .appendTo($span);
$span.click(function () { $span.click(function () {
if (typeof onSelect === "function") { if (typeof onFilePicked === "function") {
onSelect(data.href, name, data.password); onFilePicked({url: data.href, name: name, password: data.password});
} }
}); });
@ -163,21 +229,23 @@ define([
}; };
updateContainer(); updateContainer();
}; };
sframeChan.on('EV_FILE_PICKER_REFRESH', function (newFilters) {
if (Sortify(filters) !== Sortify(newFilters)) { sframeChan.on('EV_REFRESH', function (data) {
$body.html(''); if (!data) { return; }
filters = newFilters; var type = data.modal;
return void createFileDialog(); if (!create[type]) { return; }
} if (displayed && displayed.close) { displayed.close(); }
updateContainer(); else if (displayed && displayed.hide) { displayed.hide(); }
displayed = undefined;
create[type](data);
}); });
createFileDialog();
UI.removeLoadingScreen(); UI.removeLoadingScreen();
}; };
var main = function () { var main = function () {
var common; var common;
var _andThen = Util.once(andThen);
nThen(function (waitFor) { nThen(function (waitFor) {
$(waitFor(function () { $(waitFor(function () {
@ -187,11 +255,11 @@ define([
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var metadataMgr = common.getMetadataMgr(); var metadataMgr = common.getMetadataMgr();
if (metadataMgr.getMetadataLazy() !== 'uninitialized') { if (metadataMgr.getMetadataLazy() !== 'uninitialized') {
andThen(common); _andThen(common);
return; return;
} }
metadataMgr.onChange(function () { metadataMgr.onChange(function () {
andThen(common); _andThen(common);
}); });
}); });
}; };

@ -22,8 +22,8 @@ define([
}; };
window.rc = requireConfig; window.rc = requireConfig;
window.apiconf = ApiConfig; window.apiconf = ApiConfig;
$('#sbox-share-iframe').attr('src', $('#sbox-secure-iframe').attr('src',
ApiConfig.httpSafeOrigin + '/share/inner.html?' + requireConfig.urlArgs + ApiConfig.httpSafeOrigin + '/secureiframe/inner.html?' + requireConfig.urlArgs +
'#' + encodeURIComponent(JSON.stringify(req))); '#' + encodeURIComponent(JSON.stringify(req)));
// This is a cheap trick to avoid loading sframe-channel in parallel with the // This is a cheap trick to avoid loading sframe-channel in parallel with the
@ -48,7 +48,7 @@ define([
// First, we have to answer to this message, otherwise we're going to block // First, we have to answer to this message, otherwise we're going to block
// sframe-boot.js. Then we can start the channel. // sframe-boot.js. Then we can start the channel.
var msgEv = Utils.Util.mkEvent(); var msgEv = Utils.Util.mkEvent();
var iframe = $('#sbox-share-iframe')[0].contentWindow; var iframe = $('#sbox-secure-iframe')[0].contentWindow;
var postMsg = function (data) { var postMsg = function (data) {
iframe.postMessage(data, '*'); iframe.postMessage(data, '*');
}; };
@ -106,7 +106,11 @@ define([
Cryptpad.onMetadataChanged(updateMeta); Cryptpad.onMetadataChanged(updateMeta);
sframeChan.onReg('EV_METADATA_UPDATE', updateMeta); sframeChan.onReg('EV_METADATA_UPDATE', updateMeta);
config.addCommonRpc(sframeChan); config.addCommonRpc(sframeChan, true);
Cryptpad.padRpc.onMetadataEvent.reg(function (data) {
sframeChan.event('EV_RT_METADATA', data);
});
sframeChan.on('EV_CACHE_PUT', function (x) { sframeChan.on('EV_CACHE_PUT', function (x) {
Object.keys(x).forEach(function (k) { Object.keys(x).forEach(function (k) {
@ -132,11 +136,24 @@ define([
}); });
}); });
sframeChan.on('EV_SHARE_CLOSE', function () { sframeChan.on('EV_SECURE_ACTION', function (data) {
config.onClose(); config.onAction(data);
});
sframeChan.on('Q_UPLOAD_FILE', function (data, cb) {
config.onFileUpload(sframeChan, data, cb);
}); });
sframeChan.on('EV_SHARE_ACTION', function (data) {
config.onShareAction(data); sframeChan.on('Q_GET_FILES_LIST', function (types, cb) {
Cryptpad.getSecureFilesList(types, function (err, data) {
cb({
error: err,
data: data
});
});
});
sframeChan.on('EV_SECURE_IFRAME_CLOSE', function () {
config.onClose();
}); });
sframeChan.onReady(function () { sframeChan.onReady(function () {
@ -155,7 +172,7 @@ define([
}; };
return; return;
} }
sframeChan.event('EV_SHARE_REFRESH', data); sframeChan.event('EV_REFRESH', data);
cb(); cb();
}; };
return { return {

@ -1,17 +0,0 @@
@import (reference) '../../customize/src/less2/include/tippy.less';
@import (reference) '../../customize/src/less2/include/modal.less';
@import (reference) '../../customize/src/less2/include/alertify.less';
@import (reference) '../../customize/src/less2/include/checkmark.less';
@import (reference) '../../customize/src/less2/include/password-input.less';
@import (reference) '../../customize/src/less2/include/usergrid.less';
@import (reference) '../../customize/src/less2/include/modals-ui-elements.less';
&.cp-app-share {
.tippy_main();
.alertify_main();
.modals-ui-elements_main();
.checkmark_main(20px);
.password_main();
.modal_main();
.usergrid_main();
}

@ -1,30 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>CryptPad</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="no-referrer" />
<script async data-bootload="main.js" data-main="/common/boot.js?ver=1.0" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<style>
html, body {
margin: 0px;
padding: 0px;
}
#sbox-iframe {
position:fixed;
top:0px;
left:0px;
bottom:0px;
right:0px;
width:100%;
height:100%;
border:none;
margin:0;
padding:0;
overflow:hidden;
}
</style>
</head>
<body>
<iframe id="sbox-iframe">

@ -1,29 +0,0 @@
<!DOCTYPE html>
<html style="height: 100%; background: transparent;">
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script async data-bootload="/share/inner.js" data-main="/common/sframe-boot.js?ver=1.6" src="/bower_components/requirejs/require.js?ver=2.3.5"></script>
<style>
.loading-hidden { display: none; }
body #cp-loading {
display: none;
position: absolute;
top: 15vh;
bottom: 15vh;
left: 10vw;
right: 10vw;
z-index: 200000;
overflow: hidden;
}
body #cp-loading .cp-loading-container {
margin-top: 35vh;
}
body #cp-loading .cp-loading-cryptofist {
display: none;
}
</style>
</head>
<body class="cp-app-share" style="background: transparent;">
</body>
</html>

@ -1,87 +0,0 @@
define([
'jquery',
'/bower_components/nthen/index.js',
'/common/sframe-common.js',
'/common/common-ui-elements.js',
'/common/common-interface.js',
'css!/bower_components/bootstrap/dist/css/bootstrap.min.css',
'css!/bower_components/components-font-awesome/css/font-awesome.min.css',
'less!/share/app-share.less',
], function (
$,
nThen,
SFCommon,
UIElements,
UI)
{
var APP = window.APP = {};
var init = false;
var andThen = function (common) {
if (init) { return; }
init = true;
var metadataMgr = common.getMetadataMgr();
var sframeChan = common.getSframeChannel();
var hideShareDialog = function () {
sframeChan.event('EV_SHARE_CLOSE');
};
var createShareDialog = function (data) {
var priv = metadataMgr.getPrivateData();
var hashes = priv.hashes;
var origin = priv.origin;
var pathname = priv.pathname;
var f = (data && data.file) ? UIElements.createFileShareModal
: UIElements.createShareModal;
var friends = common.getFriends();
var modal = f({
origin: origin,
pathname: pathname,
password: priv.password,
isTemplate: priv.isTemplate,
hashes: hashes,
common: common,
title: data.title,
friends: friends,
onClose: function () {
hideShareDialog();
},
fileData: {
hash: hashes.fileHash,
password: priv.password
}
});
$('button.cancel').click(); // Close any existing alertify
UI.openCustomModal(modal);
};
sframeChan.on('EV_SHARE_REFRESH', function (data) {
createShareDialog(data);
});
};
var main = function () {
var common;
nThen(function (waitFor) {
$(waitFor(function () {
UI.removeLoadingScreen();
}));
SFCommon.create(waitFor(function (c) { APP.common = common = c; }));
}).nThen(function (/*waitFor*/) {
var metadataMgr = common.getMetadataMgr();
if (metadataMgr.getMetadataLazy() !== 'uninitialized') {
andThen(common);
return;
}
metadataMgr.onChange(function () {
andThen(common);
});
});
};
main();
});
Loading…
Cancel
Save