Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging

pull/1/head
ansuz 4 years ago
commit 400134d135

@ -2540,6 +2540,7 @@ define([
UIElements.onServerError = function (common, err, toolbar, cb) {
//if (["EDELETED", "EEXPIRED", "ERESTRICTED"].indexOf(err.type) === -1) { return; }
var priv = common.getMetadataMgr().getPrivateData();
var sframeChan = common.getSframeChannel();
var msg = err.type;
if (err.type === 'EEXPIRED') {
msg = Messages.expiredError;
@ -2549,10 +2550,29 @@ define([
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
} else if (err.type === 'EDELETED') {
if (priv.burnAfterReading) { return void cb(); }
if (autoStoreModal[priv.channel]) {
autoStoreModal[priv.channel].delete();
delete autoStoreModal[priv.channel];
}
// View users have the wrong seed, thay can't retireve access directly
// Version 1 hashes don't support passwords
if (!priv.readOnly && !priv.oldVersionHash) {
sframeChan.event('EV_SHARE_OPEN', {hidden: true}); // Close share modal
UIElements.displayPasswordPrompt(common, {
fromServerError: true,
loaded: err.loaded,
});
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
(cb || function () {})();
return;
}
msg = Messages.deletedError;
if (err.loaded) {
msg += Messages.errorCopy;
}
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
} else if (err.type === 'ERESTRICTED') {
msg = Messages.restrictedError;
@ -2561,7 +2581,6 @@ define([
msg = Messages.oo_deletedVersion;
if (toolbar && typeof toolbar.failed === "function") { toolbar.failed(true); }
}
var sframeChan = common.getSframeChannel();
sframeChan.event('EV_SHARE_OPEN', {hidden: true});
UI.errorLoadingScreen(msg, Boolean(err.loaded), Boolean(err.loaded));
(cb || function () {})();
@ -2570,7 +2589,10 @@ define([
UIElements.displayPasswordPrompt = function (common, cfg, isError) {
var error;
if (isError) { error = setHTML(h('p.cp-password-error'), Messages.password_error); }
var info = h('p.cp-password-info', Messages.password_info);
var info_loaded = setHTML(h('p.cp-password-info'), Messages.errorCopy);
var password = UI.passwordInput({placeholder: Messages.password_placeholder});
var $password = $(password);
var button = h('button.btn.btn-primary', Messages.password_submit);
@ -2582,6 +2604,21 @@ define([
var submit = function () {
var value = $password.find('.cp-password-input').val();
// Password-prompt called from UIElements.onServerError
if (cfg.fromServerError) {
common.getSframeChannel().query('Q_PASSWORD_CHECK', value, function (err, obj) {
if (obj && obj.error) {
console.error(obj.error);
return void UI.warn(Messages.error);
}
// On success, outer will reload the page: this is a wrong password
UIElements.displayPasswordPrompt(common, cfg, true);
});
return;
}
// Initial load
UI.addLoadingScreen({newProgress: true});
if (window.CryptPad_updateLoadingProgress) {
window.CryptPad_updateLoadingProgress({
@ -2595,6 +2632,8 @@ define([
}
});
};
$password.find('.cp-password-input').on('keydown', function (e) { if (e.which === 13) { submit(); } });
$(button).on('click', function () { submit(); });
@ -2602,12 +2641,13 @@ define([
var block = h('div#cp-loading-password-prompt', [
error,
info,
cfg.loaded ? info_loaded : undefined,
h('p.cp-password-form', [
password,
button
])
]),
]);
UI.errorLoadingScreen(block);
UI.errorLoadingScreen(block, Boolean(cfg.loaded), Boolean(cfg.loaded));
$password.find('.cp-password-input').focus();
};

@ -481,10 +481,20 @@ define([
});
};
common.isNewChannel = function (href, password, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var channel = Hash.hrefToHexChannelId(href, password);
postMessage('IS_NEW_CHANNEL', {channel: channel}, function (obj) {
var error = obj && obj.error;
if (error) { return void cb(error); }
if (!obj) { return void cb('ERROR'); }
cb (null, obj.isNew);
}, {timeout: -1});
};
// This function is used when we want to open a pad. We first need
// to check if it exists. With the cached drive, we need to wait for
// the network to be available before we can continue.
common.isNewChannel = function (href, password, _cb) {
common.hasChannelHistory = function (href, password, _cb) {
var cb = Util.once(Util.mkAsync(_cb));
var channel = Hash.hrefToHexChannelId(href, password);
var error;
@ -1182,7 +1192,6 @@ define([
} else if (mailbox && typeof(mailbox) === "object") {
m = {};
Object.keys(mailbox).forEach(function (ed) {
console.log(mailbox[ed]);
try {
m[ed] = newCrypto.encrypt(oldCrypto.decrypt(mailbox[ed], true, true));
} catch (e) {
@ -1217,6 +1226,7 @@ define([
cryptgetVal = JSON.stringify(parsed);
}
}), optsGet);
Cache.clearChannel(newSecret.channel, waitFor());
}).nThen(function (waitFor) {
optsPut.metadata.restricted = oldMetadata.restricted;
optsPut.metadata.allowed = oldMetadata.allowed;
@ -1598,6 +1608,7 @@ define([
}
}));
}));
Cache.clearChannel(newSecret.channel, waitFor());
}).nThen(function (waitFor) {
// The new rt channel is ready
// The blob uses its own encryption and doesn't need to be reencrypted
@ -2506,7 +2517,25 @@ define([
}
if (parsedNew.hashData) { oldHref = newHref; }
};
// XXX if you're in noDrive mode, check if an FS_hash is added and reload if that's the case
// Listen for login/logout in other tabs
if (rdyCfg.noDrive && !localStorage[Constants.fileHashKey]) {
window.addEventListener('storage', function (e) {
if (e.key !== Constants.fileHashKey) { return; }
// New entry added to FS_hash: drive created in another tab, reload
var o = e.oldValue;
var n = e.newValue;
if (!o && n) {
postMessage('HAS_DRIVE', null, function(obj) {
// If we're still in noDrive mode, reload
if (!obj.state) {
LocalStore.loginReload();
}
// Otherwise this worker is connected, nothing to do
});
}
});
}
window.addEventListener('storage', function (e) {
if (e.key !== Constants.userHashKey) { return; }
var o = e.oldValue;

@ -25,10 +25,12 @@ define([
var sframeChan = common.getSframeChannel();
var metadataMgr = common.getMetadataMgr();
var channel = data.channel;
var priv = metadataMgr.getPrivateData();
var channel = data.channel || priv.channel;
var owners = data.owners || [];
var pending_owners = data.pending_owners || [];
var teamOwner = data.teamId;
var title = opts.title;
opts = opts || {};
var redrawAll = function () {};
@ -115,7 +117,7 @@ define([
if (!friend) { return; }
common.mailbox.sendTo("RM_OWNER", {
channel: channel,
title: data.title,
title: data.title || title,
pending: pending
}, {
channel: friend.notifications,
@ -271,7 +273,7 @@ define([
href: data.href || data.rohref,
password: data.password,
path: isTemplate ? ['template'] : undefined,
title: data.title || '',
title: data.title || title || "",
teamId: obj.id
}, waitFor(function (err) {
if (err) { return void console.error(err); }
@ -320,6 +322,12 @@ define([
}));
}
}).nThen(function (waitFor) {
var href = data.href;
var hashes = priv.hashes || {};
var bestHash = hashes.editHash || hashes.viewHash || hashes.fileHash;
if (data.fakeHref) {
href = Hash.hashToHref(bestHash, priv.app);
}
sel.forEach(function (el) {
var curve = $(el).attr('data-curve');
if (curve === user.curvePublic) { return; }
@ -327,9 +335,9 @@ define([
if (!friend) { return; }
common.mailbox.sendTo("ADD_OWNER", {
channel: channel,
href: data.href,
password: data.password,
title: data.title
href: href,
password: data.password || priv.password,
title: data.title || title
}, {
channel: friend.notifications,
curvePublic: friend.curvePublic
@ -398,7 +406,8 @@ define([
var sframeChan = common.getSframeChannel();
var metadataMgr = common.getMetadataMgr();
var channel = data.channel;
var priv = metadataMgr.getPrivateData();
var channel = data.channel || priv.channel;
var owners = data.owners || [];
var restricted = data.restricted || false;
var allowed = data.allowed || [];

@ -452,7 +452,7 @@ define([
account.note = obj.note;
cb(obj);
});
});
}, Cache);
};
//////////////////////////////////////////////////////////////////
@ -1710,6 +1710,10 @@ define([
var onError = function (err) {
channel.bcast("PAD_ERROR", err);
if (err && err.type === "EDELETED" && Cache && Cache.clearChannel) {
Cache.clearChannel(data.channel);
}
// If this is a DELETED, EXPIRED or RESTRICTED pad, leave the channel
if (["EDELETED", "EEXPIRED", "ERESTRICTED"].indexOf(err.type) === -1) { return; }
Store.leavePad(null, data, function () {});
@ -1720,11 +1724,13 @@ define([
postMessage(clientId, "PAD_CACHE");
},
onCacheReady: function () {
channel.hasCache = true;
postMessage(clientId, "PAD_CACHE_READY");
},
onReady: function (pad) {
var padData = pad.metadata || {};
channel.data = padData;
channel.ready = true;
if (padData && padData.validateKey && store.messenger) {
store.messenger.storeValidateKey(data.channel, padData.validateKey);
}
@ -2941,6 +2947,13 @@ define([
*/
var initialized = false;
// Are we still in noDrive mode?
Store.hasDrive = function (clientId, data, cb) {
cb({
state: Boolean(store.proxy)
});
};
// If we load CryptPad for the first time from an existing pad, don't create a
// drive automatically.
var onNoDrive = function (clientId, cb) {

@ -15,6 +15,7 @@ define([
MIGRATE_ANON_DRIVE: Store.migrateAnonDrive,
PING: function (cId, data, cb) { cb(); },
CACHE_DISABLE: Store.disableCache,
HAS_DRIVE: Store.hasDrive,
// RPC
UPDATE_PIN_LIMIT: Store.updatePinLimit,
GET_PIN_LIMIT: Store.getPinLimit,

@ -78,7 +78,7 @@ define([
};
var AppConfig;
var Test;
var password, newPadPassword;
var password, newPadPassword, newPadPasswordForce;
var initialPathInDrive;
var burnAfterReading;
@ -312,6 +312,7 @@ define([
newPadPassword = Crypto.decrypt(newPad.pw, uKey);
} catch (e) { console.error(e); }
}
if (newPad.f) { newPadPasswordForce = 1; }
if (newPad.d) {
Cryptpad.fromFileData = newPad.d;
var _parsed1 = Utils.Hash.parsePadUrl(Cryptpad.fromFileData.href);
@ -319,6 +320,7 @@ define([
delete Cryptpad.fromFileData;
}
}
} catch (e) {
console.error(e, parsed.hashData.newPadOpts);
}
@ -349,7 +351,7 @@ define([
}
// We now need to check if there is a password and if we know the correct password.
// We'll use getFileSize and isNewChannel to detect incorrect passwords.
// We'll use getFileSize and hasChannelHistory to detect incorrect passwords.
// First we'll get the password value from our drive (getPadAttribute), and we'll check
// if the channel is valid. If the pad is not stored in our drive, we'll test with an
@ -397,15 +399,15 @@ define([
}
};
if (parsed.type === "file") {
// `isNewChannel` doesn't work for files (not a channel)
// `hasChannelHistory` doesn't work for files (not a channel)
// `getFileSize` is not adapted to channels because of metadata
Cryptpad.getFileSize(currentPad.href, password, function (e, size) {
next(e, size === 0);
});
return;
}
// Not a file, so we can use `isNewChannel`
Cryptpad.isNewChannel(currentPad.href, password, next);
// Not a file, so we can use `hasChannelHistory`
Cryptpad.hasChannelHistory(currentPad.href, password, next);
});
sframeChan.event("EV_PAD_PASSWORD", cfg);
};
@ -474,17 +476,25 @@ define([
password = val;
}), parsed.getUrl());
}).nThen(function (w) {
// If we've already tested this password and this is a redirect, force
if (typeof(newPadPassword) !== "undefined" && newPadPasswordForce) {
password = newPadPassword;
return void todo();
}
// If the pad is not stored and we have a newPadPassword, it probably
// comes from a notification: password prompt pre-filled
if (!password && !stored && newPadPassword) {
passwordCfg.value = newPadPassword;
}
// Pad not stored && password required: always ask for the password
if (!stored && parsed.hashData.password) {
if (!stored && parsed.hashData.password && !newPadPasswordForce) {
return void askPassword(true, passwordCfg);
}
if (parsed.type === "file") {
// `isNewChannel` doesn't work for files (not a channel)
// `hasChannelHistory` doesn't work for files (not a channel)
// `getFileSize` is not adapted to channels because of metadata
Cryptpad.getFileSize(currentPad.href, password, w(function (e, size) {
if (size !== 0) { return void todo(); }
@ -493,8 +503,8 @@ define([
}));
return;
}
// Not a file, so we can use `isNewChannel`
Cryptpad.isNewChannel(currentPad.href, password, w(function(e, isNew) {
// Not a file, so we can use `hasChannelHistory`
Cryptpad.hasChannelHistory(currentPad.href, password, w(function(e, isNew) {
if (isNew && expire && expire < (+new Date())) {
sframeChan.event("EV_EXPIRED_ERROR");
waitFor.abort();
@ -541,8 +551,7 @@ define([
if (realtime) {
// TODO we probably don't need to check again for password-protected pads
// (we use isNewChannel to test the password...)
Cryptpad.isNewChannel(currentPad.href, password, waitFor(function (e, isNew) {
Cryptpad.hasChannelHistory(currentPad.href, password, waitFor(function (e, isNew) {
if (e) { return console.error(e); }
isNewFile = Boolean(isNew);
}));
@ -608,6 +617,7 @@ define([
feedbackAllowed: Utils.Feedback.state,
isPresent: parsed.hashData && parsed.hashData.present,
isEmbed: parsed.hashData && parsed.hashData.embed,
oldVersionHash: parsed.hashData && parsed.hashData.version < 2, // password
isHistoryVersion: parsed.hashData && parsed.hashData.versionHash,
notifications: notifs,
accounts: {
@ -1674,6 +1684,45 @@ define([
});
});
sframeChan.on('Q_PASSWORD_CHECK', function (pw, cb) {
Cryptpad.isNewChannel(currentPad.href, pw, function (e, isNew) {
if (isNew === false) {
var channel = Utils.Hash.hrefToHexChannelId(currentPad.href, pw);
nThen(function (w) {
// If the pad is stored, update its data
var _secret = Utils.Hash.getSecrets(parsed.type, parsed.hash, pw);
var chan = _secret.channel;
var editH = Utils.Hash.getEditHashFromKeys(_secret);
var viewH = Utils.Hash.getViewHashFromKeys(_secret);
var href = Utils.Hash.hashToHref(editH, parsed.type);
var roHref = Utils.Hash.hashToHref(viewH, parsed.type);
Cryptpad.setPadAttribute('password', password, w(), parsed.getUrl());
Cryptpad.setPadAttribute('channel', chan, w(), parsed.getUrl());
Cryptpad.setPadAttribute('href', href, w(), parsed.getUrl());
Cryptpad.setPadAttribute('roHref', roHref, w(), parsed.getUrl());
}).nThen(function () {
// Get redirect URL
var uHash = Utils.LocalStore.getUserHash();
var uSecret = Utils.Hash.getSecrets('drive', uHash);
var uKey = uSecret.keys.cryptKey;
var url = Utils.Hash.getNewPadURL(currentPad.href, {
pw: Crypto.encrypt(pw, uKey),
f: 1
});
// redirect
window.location.href = url;
document.location.reload();
});
return;
}
cb({
error: e
});
});
});
if (cfg.messaging) {
sframeChan.on('Q_CHAT_OPENPADCHAT', function (data, cb) {
Cryptpad.universal.execCommand({

@ -565,7 +565,12 @@ MessengerUI, Messages) {
h('span.cp-button-name', Messages.accessButton)
]));
$accessBlock.click(function () {
Common.getSframeChannel().event('EV_ACCESS_OPEN');
var title = (config.title && config.title.getTitle && config.title.getTitle())
|| (config.title && config.title.defaultName)
|| "";
Common.getSframeChannel().event('EV_ACCESS_OPEN', {
title: title
});
});
toolbar.$bottomM.append($accessBlock);

@ -27,11 +27,11 @@
"padNotPinned": "Ce pad va expirer après 3 mois d'inactivité, {0}connectez-vous{1} ou {2}enregistrez-vous{3} pour le préserver.",
"anonymousStoreDisabled": "L'administrateur de cette instance de CryptPad a désactivé le drive pour les utilisateurs non enregistrés. Vous devez vous connecter pour pouvoir utiliser CryptDrive.",
"expiredError": "Ce pad a atteint sa date d'expiration est n'est donc plus disponible.",
"deletedError": "Ce pad a été supprimé par son propriétaire et n'est donc plus disponible.",
"deletedError": "Ce document a été supprimé et n'est plus disponible.",
"inactiveError": "Ce pad a été supprimé en raison de son inactivité. Appuyez sur Échap pour créer un nouveau pad.",
"chainpadError": "Une erreur critique est survenue lors de la mise à jour du contenu. Le pad est désormais en mode lecture seule afin de s'assurer que vous ne perdiez pas davantage de données.<br>Appuyez sur <em>Échap</em> pour voir le pad ou rechargez la page pour pouvoir le modifier à nouveau.",
"invalidHashError": "L'URL du document demandé n'est pas valide.",
"errorCopy": " Vous pouvez accéder au contenu en appuyant sur <em>Échap</em>.<br>Quand vous fermerez cette page, il sera définitivement supprimé.",
"errorCopy": " Vous pouvez toujours utiliser la version actuelle en mode lecture seule en appuyant sur <em>Échap</em>.",
"errorRedirectToHome": "Appuyez sur <em>Échap</em> pour retourner vers votre CryptDrive.",
"newVersionError": "Une nouvelle version de CryptPad est disponible.<br><a href='#'>Rechargez la page</a> pour utiliser la nouvelle version, ou appuyez sur Échap pour accéder au contenu actuel en <b>mode hors-ligne</b>.",
"loading": "Chargement...",
@ -604,8 +604,8 @@
"creation_expiration": "Date d'expiration",
"creation_passwordValue": "Mot de passe",
"creation_newPadModalDescription": "Cliquer sur le type de document à créer. Vous pouvez aussi utiliser les touches <b>Tab</b> pour sélectionner un type et <b>Entrée</b> pour valider.",
"password_info": "Le pad auquel vous essayez d'accéder n'existe plus ou est protégé par un mot de passe. Entrez le bon mot de passe pour accéder à son contenu.",
"password_error": "Pad introuvable !<br>Cette erreur peut provenir de deux facteurs. Soit le mot de passe est faux, soit le pad a été supprimé du serveur.",
"password_info": "Le document auquel vous essayez d'accéder n'existe plus ou est protégé par un nouveau mot de passe. Entrez le bon mot de passe pour accéder au contenu.",
"password_error": "Document introuvable<br>Cette erreur peut provenir de deux facteurs : soit le mot de passe est faux, soit le document a été détruit.",
"password_placeholder": "Tapez le mot de passe ici...",
"password_submit": "Valider",
"properties_addPassword": "Ajouter un mot de passe",

@ -29,11 +29,11 @@
"padNotPinnedVariable": "This pad will expire after {4} days of inactivity, {0}login{1} or {2}register{3} to preserve it.",
"anonymousStoreDisabled": "The webmaster of this CryptPad instance has disabled the store for anonymous users. You have to log in to be able to use CryptDrive.",
"expiredError": "This pad has reached its expiration time and is no longer available.",
"deletedError": "This pad has been deleted by its owner and is no longer available.",
"deletedError": "This document has been deleted and is no longer available.",
"inactiveError": "This pad has been deleted due to inactivity. Press Esc to create a new pad.",
"chainpadError": "A critical error occurred when updating your content. This page is in read-only mode to make sure you won't lose your work.<br>Hit <em>Esc</em> to continue to view this pad, or reload to try editing again.",
"invalidHashError": "The document you've requested has an invalid URL.",
"errorCopy": " You can still access the content by pressing <em>Esc</em>.<br>Once you close this window you will not be able to access it again.",
"errorCopy": " You can still use the current version in read-only mode by pressing <em>Esc</em>.",
"errorRedirectToHome": "Press <em>Esc</em> to be redirected to your CryptDrive.",
"newVersionError": "A new version of CryptPad is available.<br><a href='#'>Reload</a> to use the new version, or press escape to access your content in <b>offline mode</b>.",
"loading": "Loading...",
@ -619,8 +619,8 @@
"creation_expiration": "Expiration date",
"creation_passwordValue": "Password",
"creation_newPadModalDescription": "Click on a document type to create it. You can also press <b>Tab</b> to select the type and press <b>Enter</b> to confirm.",
"password_info": "The pad you're trying to open no longer exist or is protected with a password. Enter the correct password to access its content.",
"password_error": "Pad not found!<br>This error can be caused by two factors: either the password in invalid, or the pad has been deleted from the server.",
"password_info": "The document you are trying to open no longer exist or is protected with a new password. Enter the correct password to access the content.",
"password_error": "Document not found<br>This error can be caused by two factors: either the password is invalid, or the document has been destroyed.",
"password_placeholder": "Type the password here...",
"password_submit": "Submit",
"properties_addPassword": "Add a password",

@ -89,9 +89,10 @@ define([
};
// Access modal
create['access'] = function () {
create['access'] = function (data) {
require(['/common/inner/access.js'], function (Access) {
Access.getAccessModal(common, {
title: data.title,
onClose: function () {
hideIframe();
}

Loading…
Cancel
Save