Merge branch 'soon'

pull/1/head
ansuz 7 years ago
commit 4ad0ba5c4a

@ -29,7 +29,7 @@
"json.sortify": "~2.1.0", "json.sortify": "~2.1.0",
"secure-fabric.js": "secure-v1.7.9", "secure-fabric.js": "secure-v1.7.9",
"hyperjson": "~1.4.0", "hyperjson": "~1.4.0",
"chainpad-crypto": "^0.1.8", "chainpad-crypto": "^0.2.0",
"chainpad-listmap": "^0.5.0", "chainpad-listmap": "^0.5.0",
"chainpad": "^5.1.0", "chainpad": "^5.1.0",
"chainpad-netflux": "^0.7.0", "chainpad-netflux": "^0.7.0",

@ -69,8 +69,9 @@ define([], function () {
height: auto; height: auto;
margin-bottom: 2em; margin-bottom: 2em;
} }
@media screen and (max-height: 450px) { @media screen and (max-height: 500px) {
#cp-loading .cp-loading-cryptofist { #cp-loading .cp-loading-logo {
display: none;
} }
} }
#cp-loading-message { #cp-loading-message {
@ -81,6 +82,43 @@ define([], function () {
text-align: center; text-align: center;
display: none; display: none;
} }
#cp-loading-password-prompt {
font-size: 18px;
}
#cp-loading-password-prompt .cp-password-error {
color: white;
background: #9e0000;
padding: 5px;
margin-bottom: 15px;
}
#cp-loading-password-prompt .cp-password-info {
text-align: left;
margin-bottom: 15px;
}
#cp-loading-password-prompt .cp-password-form {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
}
#cp-loading-password-prompt .cp-password-form button,
#cp-loading-password-prompt .cp-password-form .cp-password-input {
background-color: #4591c4;
color: white;
border: 1px solid #4591c4;
}
#cp-loading-password-prompt .cp-password-form .cp-password-container {
flex-shrink: 1;
min-width: 0;
}
#cp-loading-password-prompt .cp-password-form input {
flex: 1;
padding: 0 5px;
min-width: 0;
text-overflow: ellipsis;
}
#cp-loading-password-prompt .cp-password-form button:hover {
background-color: #326599;
}
#cp-loading .cp-loading-spinner-container { #cp-loading .cp-loading-spinner-container {
position: relative; position: relative;
height: 100px; height: 100px;
@ -114,6 +152,24 @@ define([], function () {
max-width: 60%; max-width: 60%;
display: inline-block; display: inline-block;
} }
.cp-loading-progress {
width: 100%;
margin: 20px;
}
.cp-loading-progress p {
margin: 5px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.cp-loading-progress-bar {
height: 24px;
background: white;
}
.cp-loading-progress-bar-value {
height: 100%;
background: #5cb85c;
}
*/}).toString().slice(14, -3); */}).toString().slice(14, -3);
var urlArgs = window.location.href.replace(/^.*\?([^\?]*)$/, function (all, x) { return x; }); var urlArgs = window.location.href.replace(/^.*\?([^\?]*)$/, function (all, x) { return x; });
var elem = document.createElement('div'); var elem = document.createElement('div');

@ -154,7 +154,7 @@ define([
proxy.login_name = uname; proxy.login_name = uname;
proxy[Constants.displayNameKey] = uname; proxy[Constants.displayNameKey] = uname;
sessionStorage.createReadme = 1; sessionStorage.createReadme = 1;
if (!shouldImport) { proxy.version = 5; } if (!shouldImport) { proxy.version = 6; }
Feedback.send('REGISTRATION', true); Feedback.send('REGISTRATION', true);
} else { } else {
Feedback.send('LOGIN', true); Feedback.send('LOGIN', true);

@ -72,7 +72,7 @@ define([
]) ])
]) ])
]), ]),
h('div.cp-version-footer', "CryptPad v2.0.0 (Alpaca)") h('div.cp-version-footer', "CryptPad v2.1.0 (Badger)")
]); ]);
}; };

@ -145,16 +145,18 @@
max-height: 100px; max-height: 100px;
} }
} }
input, select {
font-size: 14px;
border: 1px solid @colortheme_form-border;
height: 26px;
background-color: @colortheme_form-bg;
color: @colortheme_form-color;
}
.cp-creation-expire { .cp-creation-expire {
.cp-creation-expire-picker { .cp-creation-expire-picker {
text-align: center; text-align: center;
input, select {
font-size: 14px;
border: 1px solid @colortheme_form-border;
height: 26px;
background-color: @colortheme_form-bg;
color: @colortheme_form-color;
}
input { input {
width: 50px; width: 50px;
margin: 0 5px; margin: 0 5px;
@ -172,6 +174,21 @@
} }
} }
} }
.cp-creation-password {
.cp-creation-password-picker {
text-align: center;
width: 100%;
.cp-password-container {
input {
width: 150px;
padding: 0 5px;
}
label {
flex: unset;
}
}
}
}
.cp-creation-settings { .cp-creation-settings {
button { button {
margin: 0; margin: 0;

@ -6,6 +6,7 @@
@import (once) './creation.less'; @import (once) './creation.less';
@import (once) './tippy.less'; @import (once) './tippy.less';
@import (once) "./checkmark.less"; @import (once) "./checkmark.less";
@import (once) "./password-input.less";
.framework_main(@bg-color, @warn-color, @color) { .framework_main(@bg-color, @warn-color, @color) {
.toolbar_main( .toolbar_main(
@ -18,11 +19,13 @@
.tokenfield_main(); .tokenfield_main();
.tippy_main(); .tippy_main();
.checkmark_main(20px); .checkmark_main(20px);
.password_main();
.creation_main( .creation_main(
@bg-color: @bg-color, @bg-color: @bg-color,
@warn-color: @warn-color, @warn-color: @warn-color,
@color: @color @color: @color
); );
font: @colortheme_app-font;
} }
.framework_min_main( .framework_min_main(
@ -39,6 +42,8 @@
.alertify_main(); .alertify_main();
.tippy_main(); .tippy_main();
.checkmark_main(20px); .checkmark_main(20px);
.password_main();
font: @colortheme_app-font;
} }

@ -0,0 +1,13 @@
.password_main() {
.cp-password-container {
display: flex;
align-items: center;
input {
flex: 1;
min-width: 0;
}
label, .fa {
margin-left: 10px;
}
}
}

@ -1081,6 +1081,7 @@ define(function () {
out.creation_expireMonths = "Mois"; out.creation_expireMonths = "Mois";
out.creation_expire1 = "Un pad <b>illimité</b> ne sera pas supprimé du serveur à moins que son propriétaire ne le décide."; out.creation_expire1 = "Un pad <b>illimité</b> ne sera pas supprimé du serveur à moins que son propriétaire ne le décide.";
out.creation_expire2 = "Un pad <b>à durée de vie</b> sera supprimé automatiquement du serveur et du CryptDrive des utilisateurs lorsque cette durée sera dépassée."; out.creation_expire2 = "Un pad <b>à durée de vie</b> sera supprimé automatiquement du serveur et du CryptDrive des utilisateurs lorsque cette durée sera dépassée.";
out.creation_password = "Ajouter un mot de passe";
out.creation_noTemplate = "Pas de modèle"; out.creation_noTemplate = "Pas de modèle";
out.creation_newTemplate = "Nouveau modèle"; out.creation_newTemplate = "Nouveau modèle";
out.creation_create = "Créer"; out.creation_create = "Créer";
@ -1092,12 +1093,20 @@ define(function () {
out.creation_ownedByOther = "Appartient à un autre utilisateur"; out.creation_ownedByOther = "Appartient à un autre utilisateur";
out.creation_noOwner = "Pas de propriétaire"; out.creation_noOwner = "Pas de propriétaire";
out.creation_expiration = "Date d'expiration"; out.creation_expiration = "Date d'expiration";
out.creation_passwordValue = "Mot de passe";
out.creation_propertiesTitle = "Disponibilité"; out.creation_propertiesTitle = "Disponibilité";
out.creation_appMenuName = "Mode avancé (Ctrl + E)"; out.creation_appMenuName = "Mode avancé (Ctrl + E)";
out.creation_newPadModalDescription = "Cliquez sur un type de pad pour le créer. Vous pouvez aussi appuyer sur <b>Tab</b> pour sélectionner un type et appuyer sur <b>Entrée</b> pour valider."; out.creation_newPadModalDescription = "Cliquez sur un type de pad pour le créer. Vous pouvez aussi appuyer sur <b>Tab</b> pour sélectionner un type et appuyer sur <b>Entrée</b> pour valider.";
out.creation_newPadModalDescriptionAdvanced = "Cochez la case si vous souhaitez voir l'écran de création de pads (pour les pads avec propriétaire ou à durée de vie). Vous pouvez appuyer sur <b>Espace</b> pour changer sa valeur."; out.creation_newPadModalDescriptionAdvanced = "Cochez la case si vous souhaitez voir l'écran de création de pads (pour les pads avec propriétaire ou à durée de vie). Vous pouvez appuyer sur <b>Espace</b> pour changer sa valeur.";
out.creation_newPadModalAdvanced = "Afficher l'écran de création de pads"; out.creation_newPadModalAdvanced = "Afficher l'écran de création de pads";
// Password prompt on the loadind screen
out.password_info = "Le pad auquel vous essayez d'accéder est protégé par un mot de passe. Entrez le bon mot de passe pour accéder à son contenu.";
out.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.";
out.password_placeholder = "Tapez le mot de passe ici...";
out.password_submit = "Valider";
out.password_show = "Afficher";
// New share modal // New share modal
out.share_linkCategory = "Partage"; out.share_linkCategory = "Partage";
out.share_linkAccess = "Droits d'accès"; out.share_linkAccess = "Droits d'accès";
@ -1111,5 +1120,12 @@ define(function () {
out.share_embedCategory = "Intégration"; out.share_embedCategory = "Intégration";
out.share_mediatagCopy = "Copier le mediatag"; out.share_mediatagCopy = "Copier le mediatag";
// Loading info
out.loading_pad_1 = "Initialisation du pad";
out.loading_pad_2 = "Chargement du contenu du pad";
out.loading_drive_1 = "Chargement des données";
out.loading_drive_2 = "Mise à jour du format des données";
out.loading_drive_3 = "Vérification de l'intégrité des données";
return out; return out;
}); });

@ -1127,6 +1127,7 @@ define(function () {
out.creation_expireMonths = "Month(s)"; out.creation_expireMonths = "Month(s)";
out.creation_expire1 = "An <b>unlimited</b> pad will not be removed from the server until its owner deletes it."; out.creation_expire1 = "An <b>unlimited</b> pad will not be removed from the server until its owner deletes it.";
out.creation_expire2 = "An <b>expiring</b> pad has a set lifetime, after which it will be automatically removed from the server and other users' CryptDrives."; out.creation_expire2 = "An <b>expiring</b> pad has a set lifetime, after which it will be automatically removed from the server and other users' CryptDrives.";
out.creation_password = "Add a password";
out.creation_noTemplate = "No template"; out.creation_noTemplate = "No template";
out.creation_newTemplate = "New template"; out.creation_newTemplate = "New template";
out.creation_create = "Create"; out.creation_create = "Create";
@ -1138,12 +1139,20 @@ define(function () {
out.creation_ownedByOther = "Owned by another user"; out.creation_ownedByOther = "Owned by another user";
out.creation_noOwner = "No owner"; out.creation_noOwner = "No owner";
out.creation_expiration = "Expiration time"; out.creation_expiration = "Expiration time";
out.creation_passwordValue = "Password";
out.creation_propertiesTitle = "Availability"; out.creation_propertiesTitle = "Availability";
out.creation_appMenuName = "Advanced mode (Ctrl + E)"; out.creation_appMenuName = "Advanced mode (Ctrl + E)";
out.creation_newPadModalDescription = "Click on a pad type to create it. You can also press <b>Tab</b> to select the type and press <b>Enter</b> to confirm."; out.creation_newPadModalDescription = "Click on a pad type to create it. You can also press <b>Tab</b> to select the type and press <b>Enter</b> to confirm.";
out.creation_newPadModalDescriptionAdvanced = "You can check the box (or press <b>Space</b> to change its value) if you want to display the pad creation screen (for owned pads, expiring pads, etc.)."; out.creation_newPadModalDescriptionAdvanced = "You can check the box (or press <b>Space</b> to change its value) if you want to display the pad creation screen (for owned pads, expiring pads, etc.).";
out.creation_newPadModalAdvanced = "Display the pad creation screen"; out.creation_newPadModalAdvanced = "Display the pad creation screen";
// Password prompt on the loadind screen
out.password_info = "The pad you're tyring to open is protected with a password. Enter the correct password to access its content.";
out.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.";
out.password_placeholder = "Type the password here...";
out.password_submit = "Submit";
out.password_show = "Show";
// New share modal // New share modal
out.share_linkCategory = "Share link"; out.share_linkCategory = "Share link";
out.share_linkAccess = "Access rights"; out.share_linkAccess = "Access rights";
@ -1157,6 +1166,12 @@ define(function () {
out.share_embedCategory = "Embed"; out.share_embedCategory = "Embed";
out.share_mediatagCopy = "Copy mediatag to clipboard"; out.share_mediatagCopy = "Copy mediatag to clipboard";
// Loading info
out.loading_pad_1 = "Initializing pad";
out.loading_pad_2 = "Loading pad content";
out.loading_drive_1 = "Loading data";
out.loading_drive_2 = "Updating data format";
out.loading_drive_3 = "Verifying data integrity";
return out; return out;
}); });

@ -1,7 +1,7 @@
{ {
"name": "cryptpad", "name": "cryptpad",
"description": "realtime collaborative visual editor with zero knowlege server", "description": "realtime collaborative visual editor with zero knowlege server",
"version": "2.0.0", "version": "2.1.0",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"chainpad-server": "^2.0.0", "chainpad-server": "^2.0.0",

@ -132,6 +132,40 @@ define([
strungJSON(orig); strungJSON(orig);
}); });
HTML_list.forEach(function (sel) {
var el = $(sel)[0];
var pred = function (el) {
if (el.nodeName === 'DIV') {
return true;
}
};
var filter = function (x) {
console.log(x);
if (x[1]['class']) {
x[1]['class'] = x[1]['class'].replace(/cke/g, '');
}
return x;
};
assert(function (cb) {
// FlatDOM output
var map = Flat.fromDOM(el, pred, filter);
// Hyperjson output
var hj = Hyperjson.fromDOM(el, pred, filter);
var x = Flat.toDOM(map);
var y = Hyperjson.toDOM(hj);
console.error(x.outerHTML);
console.error(y.outerHTML);
cb(x.outerHTML === y.outerHTML);
}, "Test equality of FlatDOM and HyperJSON");
});
// check that old hashes parse correctly // check that old hashes parse correctly
assert(function (cb) { assert(function (cb) {
//if (1) { return cb(true); } // TODO(cjd): This is a test failure which is a known bug //if (1) { return cb(true); } // TODO(cjd): This is a test failure which is a known bug
@ -223,6 +257,33 @@ define([
hd.type === 'invite'); hd.type === 'invite');
}, "test support for invite urls"); }, "test support for invite urls");
// test support for V2
assert(function (cb) {
var parsed = Hash.parsePadUrl('/pad/#/2/pad/edit/oRE0oLCtEXusRDyin7GyLGcS/');
var secret = Hash.getSecrets('pad', '/2/pad/edit/oRE0oLCtEXusRDyin7GyLGcS/');
return cb(parsed.hashData.version === 2 &&
parsed.hashData.mode === "edit" &&
parsed.hashData.type === "pad" &&
parsed.hashData.key === "oRE0oLCtEXusRDyin7GyLGcS" &&
secret.channel === "d8d51b4aea863f3f050f47f8ad261753" &&
window.nacl.util.encodeBase64(secret.keys.cryptKey) === "0Ts1M6VVEozErV2Nx/LTv6Im5SCD7io2LlhasyyBPQo=" &&
secret.keys.validateKey === "f5A1FM9Gp55tnOcM75RyHD1oxBG9ZPh9WDA7qe2Fvps=" &&
!parsed.hashData.present);
}, "test support for version 2 hash failed to parse");
assert(function (cb) {
var parsed = Hash.parsePadUrl('/pad/#/2/pad/edit/HGu0tK2od-2BBnwAz2ZNS-t4/p/embed');
var secret = Hash.getSecrets('pad', '/2/pad/edit/HGu0tK2od-2BBnwAz2ZNS-t4/p/embed', 'pewpew');
return cb(parsed.hashData.version === 2 &&
parsed.hashData.mode === "edit" &&
parsed.hashData.type === "pad" &&
parsed.hashData.key === "HGu0tK2od-2BBnwAz2ZNS-t4" &&
secret.channel === "3fb6dc93807d903aff390b5f798c92c9" &&
window.nacl.util.encodeBase64(secret.keys.cryptKey) === "EeCkGJra8eJgVu7v4Yl2Hc3yUjrgpKpxr0Lcc3bSWVs=" &&
secret.keys.validateKey === "WGkBczJf2V6vQZfAScz8V1KY6jKdoxUCckrD+E75gGE=" &&
parsed.hashData.embed &&
parsed.hashData.password);
}, "test support for password in version 2 hash failed to parse");
assert(function (cb) { assert(function (cb) {
var url = '/pad/?utm_campaign=new_comment&utm_medium=email&utm_source=thread_mailer#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI/'; var url = '/pad/?utm_campaign=new_comment&utm_medium=email&utm_source=thread_mailer#/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI/';
var secret = Hash.parsePadUrl(url); var secret = Hash.parsePadUrl(url);

@ -334,6 +334,7 @@ define([
//var cursor = editor.getCursor(); //var cursor = editor.getCursor();
//var cleanName = data.name.replace(/[\[\]]/g, ''); //var cleanName = data.name.replace(/[\[\]]/g, '');
//var text = '!['+cleanName+']('+data.url+')'; //var text = '!['+cleanName+']('+data.url+')';
// PASSWORD_FILES
var parsed = Hash.parsePadUrl(data.url); var parsed = Hash.parsePadUrl(data.url);
var hexFileName = Util.base64ToHex(parsed.hashData.channel); var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;

@ -19,7 +19,50 @@ define([
.decodeUTF8(JSON.stringify(list)))); .decodeUTF8(JSON.stringify(list))));
}; };
var getEditHashFromKeys = Hash.getEditHashFromKeys = function (chanKey, keys) { var getEditHashFromKeys = Hash.getEditHashFromKeys = function (secret) {
var version = secret.version;
var data = secret.keys;
if (version === 0) {
return secret.channel + secret.key;
}
if (version === 1) {
if (!data.editKeyStr) { return; }
return '/1/edit/' + hexToBase64(secret.channel) +
'/' + Crypto.b64RemoveSlashes(data.editKeyStr) + '/';
}
if (version === 2) {
if (!data.editKeyStr) { return; }
var pass = secret.password ? 'p/' : '';
return '/2/' + secret.type + '/edit/' + Crypto.b64RemoveSlashes(data.editKeyStr) + '/' + pass;
}
};
var getViewHashFromKeys = Hash.getViewHashFromKeys = function (secret) {
var version = secret.version;
var data = secret.keys;
if (version === 0) { return; }
if (version === 1) {
if (!data.viewKeyStr) { return; }
return '/1/view/' + hexToBase64(secret.channel) +
'/'+Crypto.b64RemoveSlashes(data.viewKeyStr)+'/';
}
if (version === 2) {
if (!data.viewKeyStr) { return; }
var pass = secret.password ? 'p/' : '';
return '/2/' + secret.type + '/view/' + Crypto.b64RemoveSlashes(data.viewKeyStr) + '/' + pass;
}
};
var getFileHashFromKeys = Hash.getFileHashFromKeys = function (secret) {
var version = secret.version;
var data = secret.keys;
if (version === 0) { return; }
if (version === 1) {
return '/1/' + hexToBase64(secret.channel) + '/' +
Crypto.b64RemoveSlashes(data.fileKeyStr) + '/';
}
};
// V1
/*var getEditHashFromKeys = Hash.getEditHashFromKeys = function (chanKey, keys) {
if (typeof keys === 'string') { if (typeof keys === 'string') {
return chanKey + keys; return chanKey + keys;
} }
@ -34,7 +77,7 @@ define([
}; };
var getFileHashFromKeys = Hash.getFileHashFromKeys = function (fileKey, cryptKey) { var getFileHashFromKeys = Hash.getFileHashFromKeys = function (fileKey, cryptKey) {
return '/1/' + hexToBase64(fileKey) + '/' + Crypto.b64RemoveSlashes(cryptKey) + '/'; return '/1/' + hexToBase64(fileKey) + '/' + Crypto.b64RemoveSlashes(cryptKey) + '/';
}; };*/
Hash.getUserHrefFromKeys = function (origin, username, pubkey) { Hash.getUserHrefFromKeys = function (origin, username, pubkey) {
return origin + '/user/#/1/' + username + '/' + pubkey.replace(/\//g, '-'); return origin + '/user/#/1/' + username + '/' + pubkey.replace(/\//g, '-');
}; };
@ -43,6 +86,24 @@ define([
return s.replace(/\/+/g, '/'); return s.replace(/\/+/g, '/');
}; };
Hash.createChannelId = function () {
var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16));
if (id.length !== 32 || /[^a-f0-9]/.test(id)) {
throw new Error('channel ids must consist of 32 hex characters');
}
return id;
};
Hash.createRandomHash = function (type, password) {
var cryptor = Crypto.createEditCryptor2(void 0, void 0, password);
return getEditHashFromKeys({
password: Boolean(password),
version: 2,
type: type,
keys: { editKeyStr: cryptor.editKeyStr }
});
};
/* /*
Version 0 Version 0
/pad/#67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy /pad/#67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy
@ -56,25 +117,56 @@ Version 1
var hashArr = fixDuplicateSlashes(hash).split('/'); var hashArr = fixDuplicateSlashes(hash).split('/');
if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) { if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) {
parsed.type = 'pad'; parsed.type = 'pad';
if (hash.slice(0,1) !== '/' && hash.length >= 56) { if (hash.slice(0,1) !== '/' && hash.length >= 56) { // Version 0
// Old hash // Old hash
parsed.channel = hash.slice(0, 32); parsed.channel = hash.slice(0, 32);
parsed.key = hash.slice(32, 56); parsed.key = hash.slice(32, 56);
parsed.version = 0; parsed.version = 0;
parsed.getHash = function () { return hash; };
return parsed; return parsed;
} }
if (hashArr[1] && hashArr[1] === '1') { var options;
if (hashArr[1] && hashArr[1] === '1') { // Version 1
parsed.version = 1; parsed.version = 1;
parsed.mode = hashArr[2]; parsed.mode = hashArr[2];
parsed.channel = hashArr[3]; parsed.channel = hashArr[3];
parsed.key = hashArr[4].replace(/-/g, '/'); parsed.key = Crypto.b64AddSlashes(hashArr[4]);
var options = hashArr.slice(5);
options = hashArr.slice(5);
parsed.present = options.indexOf('present') !== -1; parsed.present = options.indexOf('present') !== -1;
parsed.embed = options.indexOf('embed') !== -1; parsed.embed = options.indexOf('embed') !== -1;
parsed.getHash = function (opts) {
var hash = hashArr.slice(0, 5).join('/') + '/';
if (opts.embed) { hash += 'embed/'; }
if (opts.present) { hash += 'present/'; }
return hash;
};
return parsed;
}
if (hashArr[1] && hashArr[1] === '2') { // Version 2
parsed.version = 2;
parsed.app = hashArr[2];
parsed.mode = hashArr[3];
parsed.key = hashArr[4];
options = hashArr.slice(5);
parsed.password = options.indexOf('p') !== -1;
parsed.present = options.indexOf('present') !== -1;
parsed.embed = options.indexOf('embed') !== -1;
parsed.getHash = function (opts) {
var hash = hashArr.slice(0, 5).join('/') + '/';
if (parsed.password) { hash += 'p/'; }
if (opts.embed) { hash += 'embed/'; }
if (opts.present) { hash += 'present/'; }
return hash;
};
return parsed; return parsed;
} }
return parsed; return parsed;
} }
parsed.getHash = function () { return hashArr.join('/'); };
if (['media', 'file'].indexOf(type) !== -1) { if (['media', 'file'].indexOf(type) !== -1) {
parsed.type = 'file'; parsed.type = 'file';
if (hashArr[1] && hashArr[1] === '1') { if (hashArr[1] && hashArr[1] === '1') {
@ -125,17 +217,9 @@ Version 1
url += ret.type + '/'; url += ret.type + '/';
if (!ret.hashData) { return url; } if (!ret.hashData) { return url; }
if (ret.hashData.type !== 'pad') { return url + '#' + ret.hash; } if (ret.hashData.type !== 'pad') { return url + '#' + ret.hash; }
if (ret.hashData.version !== 1) { return url + '#' + ret.hash; } if (ret.hashData.version === 0) { return url + '#' + ret.hash; }
url += '#/' + ret.hashData.version + var hash = ret.hashData.getHash(options);
'/' + ret.hashData.mode + url += '#' + hash;
'/' + ret.hashData.channel.replace(/\//g, '-') +
'/' + ret.hashData.key.replace(/\//g, '-') +'/';
if (options.embed) {
url += 'embed/';
}
if (options.present) {
url += 'present/';
}
return url; return url;
}; };
@ -153,12 +237,13 @@ Version 1
return ''; return '';
}); });
idx = href.indexOf('/#'); idx = href.indexOf('/#');
if (idx === -1) { return ret; }
ret.hash = href.slice(idx + 2); ret.hash = href.slice(idx + 2);
ret.hashData = parseTypeHash(ret.type, ret.hash); ret.hashData = parseTypeHash(ret.type, ret.hash);
return ret; return ret;
}; };
var getRelativeHref = Hash.getRelativeHref = function (href) { Hash.getRelativeHref = function (href) {
if (!href) { return; } if (!href) { return; }
if (href.indexOf('#') === -1) { return; } if (href.indexOf('#') === -1) { return; }
var parsed = parsePadUrl(href); var parsed = parsePadUrl(href);
@ -170,11 +255,13 @@ Version 1
* - no argument: use the URL hash or create one if it doesn't exist * - no argument: use the URL hash or create one if it doesn't exist
* - secretHash provided: use secretHash to find the keys * - secretHash provided: use secretHash to find the keys
*/ */
Hash.getSecrets = function (type, secretHash) { Hash.getSecrets = function (type, secretHash, password) {
var secret = {}; var secret = {};
var generate = function () { var generate = function () {
secret.keys = Crypto.createEditCryptor(); secret.keys = Crypto.createEditCryptor2(void 0, void 0, password);
secret.key = Crypto.createEditCryptor().editKeyStr; secret.channel = base64ToHex(secret.keys.chanId);
secret.version = 2;
secret.type = type;
}; };
if (!secretHash && !window.location.hash) { //!/#/.test(window.location.href)) { if (!secretHash && !window.location.hash) { //!/#/.test(window.location.href)) {
generate(); generate();
@ -191,7 +278,6 @@ Version 1
parsed = pHref.hashData; parsed = pHref.hashData;
hash = pHref.hash; hash = pHref.hash;
} }
//var parsed = parsePadUrl(window.location.href);
//var hash = secretHash || window.location.hash.slice(1); //var hash = secretHash || window.location.hash.slice(1);
if (hash.length === 0) { if (hash.length === 0) {
generate(); generate();
@ -203,9 +289,10 @@ Version 1
// Old hash // Old hash
secret.channel = parsed.channel; secret.channel = parsed.channel;
secret.key = parsed.key; secret.key = parsed.key;
} secret.version = 0;
else if (parsed.version === 1) { } else if (parsed.version === 1) {
// New hash // New hash
secret.version = 1;
if (parsed.type === "pad") { if (parsed.type === "pad") {
secret.channel = base64ToHex(parsed.channel); secret.channel = base64ToHex(parsed.channel);
if (parsed.mode === 'edit') { if (parsed.mode === 'edit') {
@ -229,49 +316,63 @@ Version 1
// version 2 hashes are to be used for encrypted blobs // version 2 hashes are to be used for encrypted blobs
throw new Error("User hashes can't be opened (yet)"); throw new Error("User hashes can't be opened (yet)");
} }
} else if (parsed.version === 2) {
// New hash
secret.version = 2;
secret.type = type;
secret.password = password;
if (parsed.type === "pad") {
if (parsed.mode === 'edit') {
secret.keys = Crypto.createEditCryptor2(parsed.key, void 0, password);
secret.channel = base64ToHex(secret.keys.chanId);
secret.key = secret.keys.editKeyStr;
if (secret.channel.length !== 32 || secret.key.length !== 24) {
throw new Error("The channel key and/or the encryption key is invalid");
}
}
else if (parsed.mode === 'view') {
secret.keys = Crypto.createViewCryptor2(parsed.key, password);
secret.channel = base64ToHex(secret.keys.chanId);
if (secret.channel.length !== 32) {
throw new Error("The channel key is invalid");
}
}
} else if (parsed.type === "file") {
throw new Error("File hashes should be version 1");
} else if (parsed.type === "user") {
throw new Error("User hashes can't be opened (yet)");
}
} }
} }
return secret; return secret;
}; };
Hash.getHashes = function (channel, secret) { Hash.getHashes = function (secret) {
var hashes = {}; var hashes = {};
if (!secret.keys) { secret = JSON.parse(JSON.stringify(secret));
if (!secret.keys && !secret.key) {
console.error('e'); console.error('e');
return hashes; return hashes;
} else if (!secret.keys) {
secret.keys = {};
} }
if (secret.keys.editKeyStr) {
hashes.editHash = getEditHashFromKeys(channel, secret.keys); if (secret.keys.editKeyStr || (secret.version === 0 && secret.key)) {
hashes.editHash = getEditHashFromKeys(secret);
} }
if (secret.keys.viewKeyStr) { if (secret.keys.viewKeyStr) {
hashes.viewHash = getViewHashFromKeys(channel, secret.keys); hashes.viewHash = getViewHashFromKeys(secret);
} }
if (secret.keys.fileKeyStr) { if (secret.keys.fileKeyStr) {
hashes.fileHash = getFileHashFromKeys(channel, secret.keys.fileKeyStr); hashes.fileHash = getFileHashFromKeys(secret);
} }
return hashes; return hashes;
}; };
var createChannelId = Hash.createChannelId = function () {
var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(16));
if (id.length !== 32 || /[^a-f0-9]/.test(id)) {
throw new Error('channel ids must consist of 32 hex characters');
}
return id;
};
Hash.createRandomHash = function () {
// 16 byte channel Id
var channelId = Util.hexToBase64(createChannelId());
// 18 byte encryption key
var key = Crypto.b64RemoveSlashes(Crypto.rand64(18));
return '/1/edit/' + [channelId, key].join('/') + '/';
};
// STORAGE // STORAGE
Hash.findWeaker = function (href, recents) { Hash.findWeaker = function (href, channel, recents) {
var rHref = href || getRelativeHref(window.location.href); var parsed = parsePadUrl(href);
var parsed = parsePadUrl(rHref);
if (!parsed.hash) { return false; } if (!parsed.hash) { return false; }
var weaker; var weaker;
Object.keys(recents).some(function (id) { Object.keys(recents).some(function (id) {
@ -279,6 +380,8 @@ Version 1
var p = parsePadUrl(pad.href); var p = parsePadUrl(pad.href);
if (p.type !== parsed.type) { return; } // Not the same type if (p.type !== parsed.type) { return; } // Not the same type
if (p.hash === parsed.hash) { return; } // Same hash, not stronger if (p.hash === parsed.hash) { return; } // Same hash, not stronger
if (channel !== pad.channel) { return; } // Not the same channel
var pHash = p.hashData; var pHash = p.hashData;
var parsedHash = parsed.hashData; var parsedHash = parsed.hashData;
if (!parsedHash || !pHash) { return; } if (!parsedHash || !pHash) { return; }
@ -287,18 +390,16 @@ Version 1
if (pHash.type !== 'pad' && parsedHash.type !== 'pad') { return; } if (pHash.type !== 'pad' && parsedHash.type !== 'pad') { return; }
if (pHash.version !== parsedHash.version) { return; } if (pHash.version !== parsedHash.version) { return; }
if (pHash.channel !== parsedHash.channel) { return; }
if (pHash.mode === 'view' && parsedHash.mode === 'edit') { if (pHash.mode === 'view' && parsedHash.mode === 'edit') {
weaker = pad.href; weaker = pad;
return true; return true;
} }
return; return;
}); });
return weaker; return weaker;
}; };
var findStronger = Hash.findStronger = function (href, recents) { Hash.findStronger = function (href, channel, recents) {
var rHref = href || getRelativeHref(window.location.href); var parsed = parsePadUrl(href);
var parsed = parsePadUrl(rHref);
if (!parsed.hash) { return false; } if (!parsed.hash) { return false; }
// We can't have a stronger hash if we're already in edit mode // We can't have a stronger hash if we're already in edit mode
if (parsed.hashData && parsed.hashData.mode === 'edit') { return; } if (parsed.hashData && parsed.hashData.mode === 'edit') { return; }
@ -308,6 +409,8 @@ Version 1
var p = parsePadUrl(pad.href); var p = parsePadUrl(pad.href);
if (p.type !== parsed.type) { return; } // Not the same type if (p.type !== parsed.type) { return; } // Not the same type
if (p.hash === parsed.hash) { return; } // Same hash, not stronger if (p.hash === parsed.hash) { return; } // Same hash, not stronger
if (channel !== pad.channel) { return; } // Not the same channel
var pHash = p.hashData; var pHash = p.hashData;
var parsedHash = parsed.hashData; var parsedHash = parsed.hashData;
if (!parsedHash || !pHash) { return; } if (!parsedHash || !pHash) { return; }
@ -316,37 +419,20 @@ Version 1
if (pHash.type !== 'pad' && parsedHash.type !== 'pad') { return; } if (pHash.type !== 'pad' && parsedHash.type !== 'pad') { return; }
if (pHash.version !== parsedHash.version) { return; } if (pHash.version !== parsedHash.version) { return; }
if (pHash.channel !== parsedHash.channel) { return; }
if (pHash.mode === 'edit' && parsedHash.mode === 'view') { if (pHash.mode === 'edit' && parsedHash.mode === 'view') {
stronger = pad.href; stronger = pad;
return true; return true;
} }
return; return;
}); });
return stronger; return stronger;
}; };
Hash.isNotStrongestStored = function (href, recents) {
return findStronger(href, recents);
};
Hash.hrefToHexChannelId = function (href) { Hash.hrefToHexChannelId = function (href, password) {
var parsed = Hash.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
if (!parsed || !parsed.hash) { return; } if (!parsed || !parsed.hash) { return; }
var secret = Hash.getSecrets(parsed.type, parsed.hash, password);
parsed = parsed.hashData; return secret.channel;
if (parsed.version === 0) {
return parsed.channel;
} else if (parsed.version !== 1 && parsed.version !== 2) {
console.error("parsed href had no version");
console.error(parsed);
return;
}
var channel = parsed.channel;
if (!channel) { return; }
var hex = base64ToHex(channel);
return hex;
}; };
Hash.getBlobPathFromHex = function (id) { Hash.getBlobPathFromHex = function (id) {

@ -513,6 +513,50 @@ define([
Alertify.error(Util.fixHTML(msg)); Alertify.error(Util.fixHTML(msg));
}; };
UI.passwordInput = function (opts, displayEye) {
opts = opts || {};
var attributes = merge({
type: 'password'
}, opts);
var input = h('input.cp-password-input', attributes);
var reveal = UI.createCheckbox('cp-password-reveal', Messages.password_show);
var eye = h('span.fa.fa-eye.cp-password-reveal');
$(reveal).find('input').on('change', function () {
if($(this).is(':checked')) {
$(input).prop('type', 'text');
$(input).focus();
return;
}
$(input).prop('type', 'password');
$(input).focus();
});
$(eye).mousedown(function () {
$(input).prop('type', 'text');
$(input).focus();
}).mouseup(function(){
$(input).prop('type', 'password');
$(input).focus();
}).mouseout(function(){
$(input).prop('type', 'password');
$(input).focus();
});
if (displayEye) {
$(reveal).hide();
} else {
$(eye).hide();
}
return h('span.cp-password-container', [
input,
reveal,
eye
]);
};
/* /*
* spinner * spinner
*/ */
@ -546,6 +590,11 @@ define([
var rdm = Math.floor(Math.random() * keys.length); var rdm = Math.floor(Math.random() * keys.length);
return Messages.tips[keys[rdm]]; return Messages.tips[keys[rdm]];
};*/ };*/
var loading = {
error: false,
driveState: 0,
padState: 0
};
UI.addLoadingScreen = function (config) { UI.addLoadingScreen = function (config) {
config = config || {}; config = config || {};
var loadingText = config.loadingText; var loadingText = config.loadingText;
@ -554,11 +603,21 @@ define([
$loading.css('display', ''); $loading.css('display', '');
$loading.removeClass('cp-loading-hidden'); $loading.removeClass('cp-loading-hidden');
$('.cp-loading-spinner-container').show(); $('.cp-loading-spinner-container').show();
if (!config.noProgress && !$loading.find('.cp-loading-progress').length) {
var progress = h('div.cp-loading-progress', [
h('p.cp-loading-progress-drive'),
h('p.cp-loading-progress-pad')
]);
$loading.find('.cp-loading-container').append(progress);
} else if (config.noProgress) {
$loading.find('.cp-loading-progress').remove();
}
if (loadingText) { if (loadingText) {
$('#' + LOADING).find('p').show().text(loadingText); $('#' + LOADING).find('#cp-loading-message').show().text(loadingText);
} else { } else {
$('#' + LOADING).find('p').hide().text(''); $('#' + LOADING).find('#cp-loading-message').hide().text('');
} }
loading.error = false;
}; };
if ($('#' + LOADING).length) { if ($('#' + LOADING).length) {
todo(); todo();
@ -567,6 +626,58 @@ define([
todo(); todo();
} }
}; };
UI.updateLoadingProgress = function (data, isDrive) {
var $loading = $('#' + LOADING);
if (!$loading.length || loading.error) { return; }
var $progress;
if (isDrive) {
// Drive state
if (loading.driveState === -1) { return; } // Already loaded
$progress = $loading.find('.cp-loading-progress-drive');
if (!$progress.length) { return; } // Can't find the box to display data
// If state is -1, remove the box, drive is loaded
if (data.state === -1) {
loading.driveState = -1;
$progress.remove();
} else {
if (data.state < loading.driveState) { return; } // We should not display old data
// Update the current state
loading.driveState = data.state;
data.progress = data.progress || 100;
data.msg = Messages['loading_drive_'+data.state] || '';
$progress.html(data.msg);
if (data.progress) {
$progress.append(h('div.cp-loading-progress-bar', [
h('div.cp-loading-progress-bar-value', {style: 'width:'+data.progress+'%;'})
]));
}
}
} else {
// Pad state
if (loading.padState === -1) { return; } // Already loaded
$progress = $loading.find('.cp-loading-progress-pad');
if (!$progress.length) { return; } // Can't find the box to display data
// If state is -1, remove the box, pad is loaded
if (data.state === -1) {
loading.padState = -1;
$progress.remove();
} else {
if (data.state < loading.padState) { return; } // We should not display old data
// Update the current state
loading.padState = data.state;
data.progress = data.progress || 100;
data.msg = Messages['loading_pad_'+data.state] || '';
$progress.html(data.msg);
if (data.progress) {
$progress.append(h('div.cp-loading-progress-bar', [
h('div.cp-loading-progress-bar-value', {style: 'width:'+data.progress+'%;'})
]));
}
}
}
};
UI.removeLoadingScreen = function (cb) { UI.removeLoadingScreen = function (cb) {
// Release the test blocker, hopefully every test has been registered. // Release the test blocker, hopefully every test has been registered.
// This test is created in sframe-boot2.js // This test is created in sframe-boot2.js
@ -575,6 +686,7 @@ define([
$('#' + LOADING).addClass("cp-loading-hidden"); $('#' + LOADING).addClass("cp-loading-hidden");
setTimeout(cb, 750); setTimeout(cb, 750);
loading.error = false;
var $tip = $('#cp-loading-tip').css('top', '') var $tip = $('#cp-loading-tip').css('top', '')
// loading.less sets transition-delay: $wait-time // loading.less sets transition-delay: $wait-time
// and transition: opacity $fadeout-time // and transition: opacity $fadeout-time
@ -588,18 +700,27 @@ define([
// jquery.fadeout can get stuck // jquery.fadeout can get stuck
}; };
UI.errorLoadingScreen = function (error, transparent, exitable) { UI.errorLoadingScreen = function (error, transparent, exitable) {
if (!$('#' + LOADING).is(':visible') || $('#' + LOADING).hasClass('cp-loading-hidden')) { var $loading = $('#' + LOADING);
if (!$loading.is(':visible') || $loading.hasClass('cp-loading-hidden')) {
UI.addLoadingScreen({hideTips: true}); UI.addLoadingScreen({hideTips: true});
} }
loading.error = true;
$loading.find('.cp-loading-progress').remove();
$('.cp-loading-spinner-container').hide(); $('.cp-loading-spinner-container').hide();
$('#cp-loading-tip').remove(); $('#cp-loading-tip').remove();
if (transparent) { $('#' + LOADING).css('opacity', 0.9); } if (transparent) { $loading.css('opacity', 0.9); }
$('#' + LOADING).find('p').show().html(error || Messages.error); var $error = $loading.find('#cp-loading-message').show();
if (error instanceof Element) {
$error.html('').append(error);
} else {
$error.html(error || Messages.error);
}
if (exitable) { if (exitable) {
$(window).focus(); $(window).focus();
$(window).keydown(function (e) { $(window).keydown(function (e) {
if (e.which === 27) { if (e.which === 27) {
$('#' + LOADING).hide(); $loading.hide();
loading.error = false;
if (typeof(exitable) === "function") { exitable(); } if (typeof(exitable) === "function") { exitable(); }
} }
}); });
@ -659,12 +780,14 @@ define([
} }
}, },
//arrowType: 'round', //arrowType: 'round',
dynamicTitle: true,
arrowTransform: 'scale(2)', arrowTransform: 'scale(2)',
zIndex: 100000001 zIndex: 100000001
}); });
UI.addTooltips = function () { UI.addTooltips = function () {
var MutationObserver = window.MutationObserver; var MutationObserver = window.MutationObserver;
var addTippy = function (i, el) { var addTippy = function (i, el) {
if (el._tippy) { return; }
if (el.nodeName === 'IFRAME') { return; } if (el.nodeName === 'IFRAME') { return; }
var opts = { var opts = {
distance: 15 distance: 15

@ -99,7 +99,7 @@ define([
try { try {
var parsed = Hash.parsePadUrl(window.location.href); var parsed = Hash.parsePadUrl(window.location.href);
if (!parsed.hashData) { return; } if (!parsed.hashData) { return; }
var chan = parsed.hashData.channel; var chan = Hash.hrefToHexChannelId(window.location.href);
// Decrypt // Decrypt
var keyStr = parsed.hashData.key; var keyStr = parsed.hashData.key;
var cryptor = Crypto.createEditCryptor(keyStr); var cryptor = Crypto.createEditCryptor(keyStr);
@ -113,7 +113,7 @@ define([
if (!decryptMsg) { return; } if (!decryptMsg) { return; }
// Parse // Parse
msg = JSON.parse(decryptMsg); msg = JSON.parse(decryptMsg);
if (msg[1] !== parsed.hashData.channel) { return; } if (msg[1] !== chan) { return; }
var msgData = msg[2]; var msgData = msg[2];
var msgStr; var msgStr;
if (msg[0] === "FRIEND_REQ") { if (msg[0] === "FRIEND_REQ") {
@ -199,7 +199,7 @@ define([
var parsed = Hash.parsePadUrl(data.href); var parsed = Hash.parsePadUrl(data.href);
if (!parsed.hashData) { return; } if (!parsed.hashData) { return; }
// Message // Message
var chan = parsed.hashData.channel; var chan = Hash.hrefToHexChannelId(data.href);
var myData = createData(cfg.proxy); var myData = createData(cfg.proxy);
var msg = ["FRIEND_REQ", chan, myData]; var msg = ["FRIEND_REQ", chan, myData];
// Encryption // Encryption

@ -205,7 +205,7 @@ define([
if (content === oldThumbnailState) { return; } if (content === oldThumbnailState) { return; }
oldThumbnailState = content; oldThumbnailState = content;
Thumb.fromDOM(opts, function (err, b64) { Thumb.fromDOM(opts, function (err, b64) {
Thumb.setPadThumbnail(common, opts.href, b64); Thumb.setPadThumbnail(common, opts.href, null, b64);
}); });
}; };
var nafa = Util.notAgainForAnother(mkThumbnail, Thumb.UPDATE_INTERVAL); var nafa = Util.notAgainForAnother(mkThumbnail, Thumb.UPDATE_INTERVAL);
@ -240,20 +240,22 @@ define([
Thumb.addThumbnail = function(thumb, $span, cb) { Thumb.addThumbnail = function(thumb, $span, cb) {
return addThumbnail(null, thumb, $span, cb); return addThumbnail(null, thumb, $span, cb);
}; };
var getKey = function (href) { var getKey = function (type, channel) {
var parsed = Hash.parsePadUrl(href); return 'thumbnail-' + type + '-' + channel;
return 'thumbnail-' + parsed.type + '-' + parsed.hashData.channel;
}; };
Thumb.setPadThumbnail = function (common, href, b64, cb) { Thumb.setPadThumbnail = function (common, href, channel, b64, cb) {
cb = cb || function () {}; cb = cb || function () {};
var k = getKey(href); var parsed = Hash.parsePadUrl(href);
channel = channel || common.getMetadataMgr().getPrivateData().channel;
var k = getKey(parsed.type, channel);
common.setThumbnail(k, b64, cb); common.setThumbnail(k, b64, cb);
}; };
Thumb.displayThumbnail = function (common, href, $container, cb) { Thumb.displayThumbnail = function (common, href, channel, $container, cb) {
cb = cb || function () {}; cb = cb || function () {};
var parsed = Hash.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
var k = getKey(href); var k = getKey(parsed.type, channel);
var whenNewThumb = function () { var whenNewThumb = function () {
// PASSWORD_FILES
var secret = Hash.getSecrets('file', parsed.hash); var secret = Hash.getSecrets('file', parsed.hash);
var hexFileName = Util.base64ToHex(secret.channel); var hexFileName = Util.base64ToHex(secret.channel);
var src = Hash.getBlobPathFromHex(hexFileName); var src = Hash.getBlobPathFromHex(hexFileName);
@ -270,7 +272,7 @@ define([
if (!v) { if (!v) {
v = 'EMPTY'; v = 'EMPTY';
} }
Thumb.setPadThumbnail(common, href, v, function (err) { Thumb.setPadThumbnail(common, href, hexFileName, v, function (err) {
if (!metadata.thumbnail) { return; } if (!metadata.thumbnail) { return; }
addThumbnail(err, metadata.thumbnail, $container, cb); addThumbnail(err, metadata.thumbnail, $container, cb);
}); });

@ -60,6 +60,10 @@ define([
var getPropertiesData = function (common, cb) { var getPropertiesData = function (common, cb) {
var data = {}; var data = {};
NThen(function (waitFor) { NThen(function (waitFor) {
common.getPadAttribute('password', waitFor(function (err, val) {
data.password = val;
}));
}).nThen(function (waitFor) {
common.getPadAttribute('href', waitFor(function (err, val) { common.getPadAttribute('href', waitFor(function (err, val) {
var base = common.getMetadataMgr().getPrivateData().origin; var base = common.getMetadataMgr().getPrivateData().origin;
@ -71,15 +75,19 @@ define([
// We're not in a read-only pad // We're not in a read-only pad
data.href = base + val; data.href = base + val;
// Get Read-only href // Get Read-only href
if (parsed.hashData.type !== "pad") { return; } if (parsed.hashData.type !== "pad") { return; }
var i = data.href.indexOf('#') + 1; var i = data.href.indexOf('#') + 1;
var hBase = data.href.slice(0, i); var hBase = data.href.slice(0, i);
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash); var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
if (!hrefsecret.keys) { return; } if (!hrefsecret.keys) { return; }
var viewHash = Hash.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys); var viewHash = Hash.getViewHashFromKeys(hrefsecret);
data.roHref = hBase + viewHash; data.roHref = hBase + viewHash;
})); }));
common.getPadAttribute('channel', waitFor(function (err, val) {
data.channel = val;
}));
common.getPadAttribute('atime', waitFor(function (err, val) { common.getPadAttribute('atime', waitFor(function (err, val) {
data.atime = val; data.atime = val;
})); }));
@ -135,6 +143,22 @@ define([
$d.append(UI.dialog.selectable(expire, { $d.append(UI.dialog.selectable(expire, {
id: 'cp-app-prop-expire', id: 'cp-app-prop-expire',
})); }));
if (typeof data.password !== "undefined") {
$('<label>', {'for': 'cp-app-prop-password'}).text(Messages.creation_passwordValue)
.appendTo($d);
var password = UI.passwordInput({
id: 'cp-app-prop-expire',
readonly: 'readonly'
});
var $pwInput = $(password).find('.cp-password-input');
$pwInput.val(data.password).click(function () {
$pwInput[0].select();
});
$(password).find('.cp-checkmark').css('margin-bottom', '15px');
$d.append(password);
}
cb(void 0, $d); cb(void 0, $d);
}; };
var getPadProperties = function (common, data, cb) { var getPadProperties = function (common, data, cb) {
@ -176,7 +200,7 @@ define([
if (common.isLoggedIn() && AppConfig.enablePinning) { if (common.isLoggedIn() && AppConfig.enablePinning) {
// check the size of this file... // check the size of this file...
common.getFileSize(data.href, function (e, bytes) { common.getFileSize(data.channel, function (e, bytes) {
if (e) { if (e) {
// there was a problem with the RPC // there was a problem with the RPC
console.error(e); console.error(e);
@ -926,14 +950,14 @@ define([
}; };
}; };
var setHTML = function (e, html) {
e.innerHTML = html;
return e;
};
UIElements.createHelpMenu = function (common, categories) { UIElements.createHelpMenu = function (common, categories) {
var type = common.getMetadataMgr().getMetadata().type || 'pad'; var type = common.getMetadataMgr().getMetadata().type || 'pad';
var setHTML = function (e, html) {
e.innerHTML = html;
return e;
};
var elements = []; var elements = [];
if (Messages.help && Messages.help.generic) { if (Messages.help && Messages.help.generic) {
Object.keys(Messages.help.generic).forEach(function (el) { Object.keys(Messages.help.generic).forEach(function (el) {
@ -1135,12 +1159,13 @@ define([
}; };
return; return;
} }
// No password for avatars
var secret = Hash.getSecrets('file', parsed.hash); var secret = Hash.getSecrets('file', parsed.hash);
if (secret.keys && secret.channel) { if (secret.keys && secret.channel) {
var cryptKey = secret.keys && secret.keys.fileKeyStr; var cryptKey = secret.keys && secret.keys.fileKeyStr;
var hexFileName = Util.base64ToHex(secret.channel); var hexFileName = Util.base64ToHex(secret.channel);
var src = Hash.getBlobPathFromHex(hexFileName); var src = Hash.getBlobPathFromHex(hexFileName);
Common.getFileSize(href, function (e, data) { Common.getFileSize(hexFileName, function (e, data) {
if (e) { if (e) {
displayDefault(); displayDefault();
return void console.error(e); return void console.error(e);
@ -1907,6 +1932,18 @@ define([
createHelper('/faq.html#keywords-expiring', Messages.creation_expire2), createHelper('/faq.html#keywords-expiring', Messages.creation_expire2),
]); ]);
// Password
var password = h('div.cp-creation-password', [
UI.createCheckbox('cp-creation-password', Messages.creation_password, false),
h('span.cp-creation-password-picker.cp-creation-slider', [
UI.passwordInput({id: 'cp-creation-password-val'})
/*h('input#cp-creation-password-val', {
type: "text" // TODO type password with click to show
}),*/
]),
//createHelper('#', "TODO: password protection adds another layer of security ........") // TODO
]);
var right = h('span.fa.fa-chevron-right.cp-creation-template-more'); var right = h('span.fa.fa-chevron-right.cp-creation-template-more');
var left = h('span.fa.fa-chevron-left.cp-creation-template-more'); var left = h('span.fa.fa-chevron-left.cp-creation-template-more');
var templates = h('div.cp-creation-template', [ var templates = h('div.cp-creation-template', [
@ -1932,6 +1969,7 @@ define([
$(h('div#cp-creation-form', [ $(h('div#cp-creation-form', [
owned, owned,
expire, expire,
password,
settings, settings,
templates, templates,
createDiv createDiv
@ -2042,6 +2080,19 @@ define([
$creation.focus(); $creation.focus();
}); });
// Display expiration form when checkbox checked
$creation.find('#cp-creation-password').on('change', function () {
if ($(this).is(':checked')) {
$creation.find('.cp-creation-password-picker:not(.active)').addClass('active');
$creation.find('.cp-creation-password:not(.active)').addClass('active');
$creation.find('#cp-creation-password-val').focus();
return;
}
$creation.find('.cp-creation-password-picker').removeClass('active');
$creation.find('.cp-creation-password').removeClass('active');
$creation.focus();
});
// Display settings help when checkbox checked // Display settings help when checkbox checked
$creation.find('#cp-creation-remember').on('change', function () { $creation.find('#cp-creation-remember').on('change', function () {
if ($(this).is(':checked')) { if ($(this).is(':checked')) {
@ -2090,12 +2141,16 @@ define([
} }
expireVal = ($('#cp-creation-expire-val').val() || 0) * unit; expireVal = ($('#cp-creation-expire-val').val() || 0) * unit;
} }
// Password
var passwordVal = $('#cp-creation-password').is(':checked') ?
$('#cp-creation-password-val').val() : undefined;
var $template = $creation.find('.cp-creation-template-selected'); var $template = $creation.find('.cp-creation-template-selected');
var templateId = $template.data('id') || undefined; var templateId = $template.data('id') || undefined;
return { return {
owned: ownedVal, owned: ownedVal,
password: passwordVal,
expire: expireVal, expire: expireVal,
templateId: templateId templateId: templateId
}; };
@ -2165,5 +2220,38 @@ define([
(cb || function () {})(); (cb || function () {})();
}; };
UIElements.displayPasswordPrompt = function (common, 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 password = UI.passwordInput({placeholder: Messages.password_placeholder});
var button = h('button', Messages.password_submit);
var submit = function () {
var value = $(password).find('.cp-password-input').val();
UI.addLoadingScreen();
common.getSframeChannel().query('Q_PAD_PASSWORD_VALUE', value, function (err, data) {
if (!data) {
UIElements.displayPasswordPrompt(common, true);
}
});
};
$(password).find('.cp-password-input').on('keydown', function (e) { if (e.which === 13) { submit(); } });
$(button).on('click', function () { submit(); });
var block = h('div#cp-loading-password-prompt', [
error,
info,
h('p.cp-password-form', [
password,
button
])
]);
UI.errorLoadingScreen(block);
$(password).find('.cp-password-input').focus();
};
return UIElements; return UIElements;
}); });

@ -20,9 +20,9 @@ define([
} }
}; };
var makeConfig = function (hash) { var makeConfig = function (hash, password) {
// We can't use cryptget with a file or a user so we can use 'pad' as hash type // We can't use cryptget with a file or a user so we can use 'pad' as hash type
var secret = Hash.getSecrets('pad', hash); var secret = Hash.getSecrets('pad', hash, password);
if (!secret.keys) { secret.keys = secret.key; } // support old hashses if (!secret.keys) { secret.keys = secret.key; } // support old hashses
var config = { var config = {
websocketURL: NetConfig.getWebsocketURL(), websocketURL: NetConfig.getWebsocketURL(),
@ -47,8 +47,10 @@ define([
if (typeof(cb) !== 'function') { if (typeof(cb) !== 'function') {
throw new Error('Cryptget expects a callback'); throw new Error('Cryptget expects a callback');
} }
opt = opt || {};
var config = makeConfig(hash, opt.password);
var Session = { cb: cb, }; var Session = { cb: cb, };
var config = makeConfig(hash);
config.onReady = function (info) { config.onReady = function (info) {
var rt = Session.session = info.realtime; var rt = Session.session = info.realtime;
@ -64,9 +66,11 @@ define([
if (typeof(cb) !== 'function') { if (typeof(cb) !== 'function') {
throw new Error('Cryptput expects a callback'); throw new Error('Cryptput expects a callback');
} }
opt = opt || {};
var config = makeConfig(hash); var config = makeConfig(hash, opt.password);
var Session = { cb: cb, }; var Session = { cb: cb, };
config.onReady = function (info) { config.onReady = function (info) {
var realtime = Session.session = info.realtime; var realtime = Session.session = info.realtime;
Session.network = info.network; Session.network = info.network;

@ -246,8 +246,8 @@ define([
}); });
}; };
common.getFileSize = function (href, cb) { common.getFileSize = function (href, password, cb) {
postMessage("GET_FILE_SIZE", {href: href}, function (obj) { postMessage("GET_FILE_SIZE", {href: href, password: password}, function (obj) {
if (obj && obj.error) { return void cb(obj.error); } if (obj && obj.error) { return void cb(obj.error); }
cb(undefined, obj.size); cb(undefined, obj.size);
}); });
@ -260,8 +260,8 @@ define([
}); });
}; };
common.isNewChannel = function (href, cb) { common.isNewChannel = function (href, password, cb) {
postMessage('IS_NEW_CHANNEL', {href: href}, function (obj) { postMessage('IS_NEW_CHANNEL', {href: href, password: password}, function (obj) {
if (obj.error) { return void cb(obj.error); } if (obj.error) { return void cb(obj.error); }
if (!obj) { return void cb('INVALID_RESPONSE'); } if (!obj) { return void cb('INVALID_RESPONSE'); }
cb(undefined, obj.isNew); cb(undefined, obj.isNew);
@ -395,8 +395,10 @@ define([
common.saveAsTemplate = function (Cryptput, data, cb) { common.saveAsTemplate = function (Cryptput, data, cb) {
var p = Hash.parsePadUrl(window.location.href); var p = Hash.parsePadUrl(window.location.href);
if (!p.type) { return; } if (!p.type) { return; }
var hash = Hash.createRandomHash(); // PPP: password for the new template?
var hash = Hash.createRandomHash(p.type);
var href = '/' + p.type + '/#' + hash; var href = '/' + p.type + '/#' + hash;
// PPP: add password as cryptput option
Cryptput(hash, data.toSave, function (e) { Cryptput(hash, data.toSave, function (e) {
if (e) { throw new Error(e); } if (e) { throw new Error(e); }
postMessage("ADD_PAD", { postMessage("ADD_PAD", {
@ -419,16 +421,33 @@ define([
}); });
}; };
common.useTemplate = function (href, Crypt, cb, opts) { common.useTemplate = function (href, Crypt, cb, optsPut) {
// opts is used to overrides options for chainpad-netflux in cryptput // opts is used to overrides options for chainpad-netflux in cryptput
// it allows us to add owners and expiration time if it is a new file // it allows us to add owners and expiration time if it is a new file
var parsed = Hash.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
var parsed2 = Hash.parsePadUrl(window.location.href);
if(!parsed) { throw new Error("Cannot get template hash"); } if(!parsed) { throw new Error("Cannot get template hash"); }
postMessage("INCREMENT_TEMPLATE_USE", href); postMessage("INCREMENT_TEMPLATE_USE", href);
Crypt.get(parsed.hash, function (err, val) {
if (err) { throw new Error(err); } optsPut = optsPut || {};
var p = Hash.parsePadUrl(window.location.href); var optsGet = {};
Crypt.put(p.hash, val, cb, opts); Nthen(function (waitFor) {
if (parsed.hashData && parsed.hashData.password) {
common.getPadAttribute('password', waitFor(function (err, password) {
optsGet.password = password;
}), href);
}
if (parsed2.hashData && parsed2.hashData.password) {
common.getPadAttribute('password', waitFor(function (err, password) {
optsPut.password = password;
}));
}
}).nThen(function () {
Crypt.get(parsed.hash, function (err, val) {
if (err) { throw new Error(err); }
Crypt.put(parsed2.hash, val, cb, optsPut);
}, optsGet);
}); });
}; };
@ -439,20 +458,18 @@ define([
}; };
// When opening a new pad or renaming it, store the new title // When opening a new pad or renaming it, store the new title
common.setPadTitle = function (title, padHref, path, cb) { common.setPadTitle = function (data, cb) {
var href = padHref || window.location.href; if (!data || typeof (data) !== "object") { return cb ('Data is not an object'); }
var href = data.href || window.location.href;
var parsed = Hash.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
if (!parsed.hash) { return; } if (!parsed.hash) { return cb ('Invalid hash'); }
href = parsed.getUrl({present: parsed.present}); data.href = parsed.getUrl({present: parsed.present});
if (title === null) { return; } if (typeof (data.title) !== "string") { return cb('Missing title'); }
if (title.trim() === "") { title = Hash.getDefaultName(parsed); } if (data.title.trim() === "") { data.title = Hash.getDefaultName(parsed); }
postMessage("SET_PAD_TITLE", { postMessage("SET_PAD_TITLE", data, function (obj) {
href: href,
title: title,
path: path
}, function (obj) {
if (obj && obj.error) { if (obj && obj.error) {
console.log("unable to set pad title"); console.log("unable to set pad title");
return void cb(obj.error); return void cb(obj.error);
@ -473,10 +490,6 @@ define([
cb(void 0, data); cb(void 0, data);
}); });
}; };
// Set initial path when creating a pad from pad creation screen
common.setInitialPath = function (path) {
postMessage("SET_INITIAL_PATH", path);
};
// Messaging (manage friends from the userlist) // Messaging (manage friends from the userlist)
common.inviteFromUserlist = function (netfluxId, cb) { common.inviteFromUserlist = function (netfluxId, cb) {
@ -549,6 +562,10 @@ define([
pad.onDisconnectEvent = Util.mkEvent(); pad.onDisconnectEvent = Util.mkEvent();
pad.onErrorEvent = Util.mkEvent(); pad.onErrorEvent = Util.mkEvent();
// Loading events
common.loading = {};
common.loading.onDriveEvent = Util.mkEvent();
common.getFullHistory = function (data, cb) { common.getFullHistory = function (data, cb) {
postMessage("GET_FULL_HISTORY", data, cb); postMessage("GET_FULL_HISTORY", data, cb);
}; };
@ -556,15 +573,15 @@ define([
common.getShareHashes = function (secret, cb) { common.getShareHashes = function (secret, cb) {
var hashes; var hashes;
if (!window.location.hash) { if (!window.location.hash) {
hashes = Hash.getHashes(secret.channel, secret); hashes = Hash.getHashes(secret);
return void cb(null, hashes); return void cb(null, hashes);
} }
var parsed = Hash.parsePadUrl(window.location.href); var parsed = Hash.parsePadUrl(window.location.href);
if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); } if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); }
if (parsed.type === 'file') { secret.channel = Util.base64ToHex(secret.channel); } if (parsed.type === 'file') { secret.channel = Util.base64ToHex(secret.channel); }
hashes = Hash.getHashes(secret.channel, secret); hashes = Hash.getHashes(secret);
if (!hashes.editHash && !hashes.viewHash && parsed.hashData && !parsed.hashData.mode) { if (secret.version === 0) {
// It means we're using an old hash // It means we're using an old hash
hashes.editHash = window.location.hash.slice(1); hashes.editHash = window.location.hash.slice(1);
return void cb(null, hashes); return void cb(null, hashes);
@ -576,7 +593,8 @@ define([
} }
postMessage("GET_STRONGER_HASH", { postMessage("GET_STRONGER_HASH", {
href: window.location.href href: window.location.href,
password: secret.password
}, function (hash) { }, function (hash) {
if (hash) { hashes.editHash = hash; } if (hash) { hashes.editHash = hash; }
cb(null, hashes); cb(null, hashes);
@ -712,6 +730,10 @@ define([
case 'DELETE_ACCOUNT': { case 'DELETE_ACCOUNT': {
common.startAccountDeletion(cb); break; common.startAccountDeletion(cb); break;
} }
// Loading
case 'LOADING_DRIVE': {
common.loading.onDriveEvent.fire(data); break;
}
} }
}; };
@ -813,18 +835,18 @@ define([
window.onhashchange = function (ev) { window.onhashchange = function (ev) {
if (ev && ev.reset) { oldHref = document.location.href; return; } if (ev && ev.reset) { oldHref = document.location.href; return; }
var newHref = document.location.href; var newHref = document.location.href;
var parsedOld = Hash.parsePadUrl(oldHref).hashData;
var parsedNew = Hash.parsePadUrl(newHref).hashData; // Compare the URLs without /embed and /present
if (parsedOld && parsedNew && ( var parsedOld = Hash.parsePadUrl(oldHref);
parsedOld.type !== parsedNew.type var parsedNew = Hash.parsePadUrl(newHref);
|| parsedOld.channel !== parsedNew.channel if (parsedOld.hashData && parsedNew.hashData &&
|| parsedOld.mode !== parsedNew.mode parsedOld.getUrl() !== parsedNew.getUrl()) {
|| parsedOld.key !== parsedNew.key)) { if (!parsedOld.hashData.key) { oldHref = newHref; return; }
if (!parsedOld.channel) { oldHref = newHref; return; } // If different, reload
document.location.reload(); document.location.reload();
return; return;
} }
if (parsedNew) { oldHref = newHref; } if (parsedNew.hashData) { oldHref = newHref; }
}; };
// Listen for login/logout in other tabs // Listen for login/logout in other tabs
window.addEventListener('storage', function (e) { window.addEventListener('storage', function (e) {

@ -41,6 +41,7 @@ define([
}; };
renderer.image = function (href, title, text) { renderer.image = function (href, title, text) {
if (href.slice(0,6) === '/file/') { if (href.slice(0,6) === '/file/') {
// PASSWORD_FILES
var parsed = Hash.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
var hexFileName = Util.base64ToHex(parsed.hashData.channel); var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;

@ -17,8 +17,7 @@ define([], function () {
return data; return data;
}; };
var identity = function (x) { return x; }; Flat.fromDOM = function (dom, predicate, filter) {
Flat.fromDOM = function (dom) {
var data = { var data = {
map: {}, map: {},
}; };
@ -34,14 +33,21 @@ define([], function () {
return id; return id;
} }
if (!el || !el.attributes) { return; } if (!el || !el.attributes) { return; }
if (predicate) {
if (!predicate(el)) { return; } // shortcircuit
}
id = uid(); id = uid();
data.map[id] = [ var temp = [
el.tagName, el.tagName,
getAttrs(el), getAttrs(el),
slice(el.childNodes).map(function (e) { slice(el.childNodes).map(function (e) {
return process(e); return process(e);
}).filter(identity) }).filter(Boolean)
]; ];
data.map[id] = filter? filter(temp): temp;
return id; return id;
}; };

@ -122,21 +122,15 @@ define([
// Do not migrate a pad if we already have it, it would create a duplicate in the drive // Do not migrate a pad if we already have it, it would create a duplicate in the drive
if (newHrefs.indexOf(href) !== -1) { return; } if (newHrefs.indexOf(href) !== -1) { return; }
// If we have a stronger version, do not add the current href // If we have a stronger version, do not add the current href
if (Hash.findStronger(href, newRecentPads)) { return; } if (Hash.findStronger(href, oldRecentPads[id].channel, newRecentPads)) { return; }
// If we have a weaker version, replace the href by the new one // If we have a weaker version, replace the href by the new one
// NOTE: if that weaker version is in the trash, the strong one will be put in unsorted // NOTE: if that weaker version is in the trash, the strong one will be put in unsorted
var weaker = Hash.findWeaker(href, newRecentPads); var weaker = Hash.findWeaker(href, oldRecentPads[id].channel, newRecentPads);
if (weaker) { if (weaker) {
// Update RECENTPADS // Update RECENTPADS
newRecentPads.some(function (pad) { weaker.href = href;
if (pad.href === weaker) {
pad.href = href;
return true;
}
return;
});
// Update the file in the drive // Update the file in the drive
newFo.replace(weaker, href); newFo.replace(weaker.href, href);
return; return;
} }
// Here it means we have a new href, so we should add it to the drive at its old location // Here it means we have a new href, so we should add it to the drive at its old location

@ -1,106 +1,152 @@
define(['/common/common-feedback.js'], function (Feedback) { define([
'/common/common-feedback.js',
'/common/common-hash.js',
'/common/common-util.js',
'/bower_components/nthen/index.js',
], function (Feedback, Hash, Util, nThen) {
// Start migration check // Start migration check
// Versions: // Versions:
// 1: migrate pad attributes // 1: migrate pad attributes
// 2: migrate indent settings (codemirror) // 2: migrate indent settings (codemirror)
return function (userObject) { return function (userObject, cb, progress) {
var version = userObject.version || 0; var version = userObject.version || 0;
// DEPRECATED nThen(function () {
// Migration 1: pad attributes moved to filesData // DEPRECATED
var migratePadAttributesToData = function () { // Migration 1: pad attributes moved to filesData
return true; var migratePadAttributesToData = function () {
}; return true;
if (version < 1) { };
migratePadAttributesToData(); if (version < 1) {
} migratePadAttributesToData();
// Migration 2: global attributes from root to 'settings' subobjects
var migrateAttributes = function () {
var drawer = 'cryptpad.userlist-drawer';
var polls = 'cryptpad.hide_poll_text';
var indentKey = 'cryptpad.indentUnit';
var useTabsKey = 'cryptpad.indentWithTabs';
var settings = userObject.settings = userObject.settings || {};
if (typeof(userObject[indentKey]) !== "undefined") {
settings.codemirror = settings.codemirror || {};
settings.codemirror.indentUnit = userObject[indentKey];
delete userObject[indentKey];
}
if (typeof(userObject[useTabsKey]) !== "undefined") {
settings.codemirror = settings.codemirror || {};
settings.codemirror.indentWithTabs = userObject[useTabsKey];
delete userObject[useTabsKey];
} }
if (typeof(userObject[drawer]) !== "undefined") { }).nThen(function () {
settings.toolbar = settings.toolbar || {}; // Migration 2: global attributes from root to 'settings' subobjects
settings.toolbar['userlist-drawer'] = userObject[drawer]; var migrateAttributes = function () {
delete userObject[drawer]; var drawer = 'cryptpad.userlist-drawer';
var polls = 'cryptpad.hide_poll_text';
var indentKey = 'cryptpad.indentUnit';
var useTabsKey = 'cryptpad.indentWithTabs';
var settings = userObject.settings = userObject.settings || {};
if (typeof(userObject[indentKey]) !== "undefined") {
settings.codemirror = settings.codemirror || {};
settings.codemirror.indentUnit = userObject[indentKey];
delete userObject[indentKey];
}
if (typeof(userObject[useTabsKey]) !== "undefined") {
settings.codemirror = settings.codemirror || {};
settings.codemirror.indentWithTabs = userObject[useTabsKey];
delete userObject[useTabsKey];
}
if (typeof(userObject[drawer]) !== "undefined") {
settings.toolbar = settings.toolbar || {};
settings.toolbar['userlist-drawer'] = userObject[drawer];
delete userObject[drawer];
}
if (typeof(userObject[polls]) !== "undefined") {
settings.poll = settings.poll || {};
settings.poll['hide-text'] = userObject[polls];
delete userObject[polls];
}
};
if (version < 2) {
migrateAttributes();
Feedback.send('Migrate-2', true);
userObject.version = version = 2;
} }
if (typeof(userObject[polls]) !== "undefined") { }).nThen(function () {
settings.poll = settings.poll || {}; // Migration 3: language from localStorage to settings
settings.poll['hide-text'] = userObject[polls]; var migrateLanguage = function () {
delete userObject[polls]; if (!localStorage.CRYPTPAD_LANG) { return; }
var l = localStorage.CRYPTPAD_LANG;
userObject.settings.language = l;
};
if (version < 3) {
migrateLanguage();
Feedback.send('Migrate-3', true);
userObject.version = version = 3;
} }
}; }).nThen(function () {
if (version < 2) { // Migration 4: allowUserFeedback to settings
migrateAttributes(); var migrateFeedback = function () {
Feedback.send('Migrate-2', true); var settings = userObject.settings = userObject.settings || {};
userObject.version = version = 2; if (typeof(userObject['allowUserFeedback']) !== "undefined") {
} settings.general = settings.general || {};
settings.general.allowUserFeedback = userObject['allowUserFeedback'];
delete userObject['allowUserFeedback'];
}
// Migration 3: language from localStorage to settings };
var migrateLanguage = function () { if (version < 4) {
if (!localStorage.CRYPTPAD_LANG) { return; } migrateFeedback();
var l = localStorage.CRYPTPAD_LANG; Feedback.send('Migrate-4', true);
userObject.settings.language = l; userObject.version = version = 4;
};
if (version < 3) {
migrateLanguage();
Feedback.send('Migrate-3', true);
userObject.version = version = 3;
}
// Migration 4: allowUserFeedback to settings
var migrateFeedback = function () {
var settings = userObject.settings = userObject.settings || {};
if (typeof(userObject['allowUserFeedback']) !== "undefined") {
settings.general = settings.general || {};
settings.general.allowUserFeedback = userObject['allowUserFeedback'];
delete userObject['allowUserFeedback'];
} }
}; }).nThen(function () {
if (version < 4) { // Migration 5: dates to Number
migrateFeedback(); var migrateDates = function () {
Feedback.send('Migrate-4', true); var data = userObject.drive && userObject.drive.filesData;
userObject.version = version = 4; if (data) {
} for (var id in data) {
if (typeof data[id].ctime !== "number") {
data[id].ctime = +new Date(data[id].ctime);
}
// Migration 5: dates to Number if (typeof data[id].atime !== "number") {
var migrateDates = function () { data[id].atime = +new Date(data[id].atime);
var data = userObject.drive && userObject.drive.filesData; }
if (data) {
for (var id in data) {
if (typeof data[id].ctime !== "number") {
data[id].ctime = +new Date(data[id].ctime);
}
if (typeof data[id].atime !== "number") {
data[id].atime = +new Date(data[id].atime);
} }
} }
};
if (version < 5) {
migrateDates();
Feedback.send('Migrate-5', true);
userObject.version = version = 5;
}
}).nThen(function (waitFor) {
var addChannelId = function () {
var data = userObject.drive.filesData;
var el, parsed;
var n = nThen(function () {});
var padsLength = Object.keys(data).length;
Object.keys(data).forEach(function (k, i) {
n = n.nThen(function (w) {
setTimeout(w(function () {
el = data[k];
parsed = Hash.parsePadUrl(el.href);
if (!el.href) { return; }
if (!el.channel) {
if (parsed.hashData && parsed.hashData.type === "file") {
// PASSWORD_FILES
el.channel = Util.base64ToHex(parsed.hashData.channel);
} else {
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
el.channel = secret.channel;
}
progress(6, Math.round(100*i/padsLength));
console.log('Adding missing channel in filesData ', el.channel);
}
}));
});
});
n.nThen(waitFor());
};
if (version < 6) {
addChannelId();
Feedback.send('Migrate-6', true);
userObject.version = version = 6;
} }
}; /*}).nThen(function (waitFor) {
if (version < 5) { // Test progress bar in the loading screen
migrateDates(); var i = 0;
Feedback.send('Migrate-5', true); var w = waitFor();
userObject.version = version = 5; var it = setInterval(function () {
} i += 5;
if (i >= 100) { w(); clearInterval(it); i = 100;}
progress(0, i);
}, 500);
progress(0, 0);*/
}).nThen(function () {
cb();
});
}; };
}); });

@ -68,8 +68,9 @@ define([
var userHash = storeHash; var userHash = storeHash;
if (!userHash) { return null; } if (!userHash) { return null; }
var userParsedHash = Hash.parseTypeHash('drive', userHash); // No password for drive
var userChannel = userParsedHash && userParsedHash.channel; var secret = Hash.getSecrets('drive', userHash);
var userChannel = secret.channel;
if (!userChannel) { return null; } if (!userChannel) { return null; }
// Get the list of pads' channel ID in your drive // Get the list of pads' channel ID in your drive
@ -81,16 +82,16 @@ define([
var d = store.userObject.getFileData(id); var d = store.userObject.getFileData(id);
if (d.owners && d.owners.length && edPublic && if (d.owners && d.owners.length && edPublic &&
d.owners.indexOf(edPublic) === -1) { return; } d.owners.indexOf(edPublic) === -1) { return; }
return Hash.hrefToHexChannelId(d.href); return d.channel;
}) })
.filter(function (x) { return x; }); .filter(function (x) { return x; });
// Get the avatar // Get the avatar
var profile = store.proxy.profile; var profile = store.proxy.profile;
if (profile) { if (profile) {
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit) : null; var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null;
if (profileChan) { list.push(profileChan); } if (profileChan) { list.push(profileChan); }
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar) : null; var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar, null) : null;
if (avatarChan) { list.push(avatarChan); } if (avatarChan) { list.push(avatarChan); }
} }
@ -99,7 +100,7 @@ define([
list = list.concat(fList); list = list.concat(fList);
} }
list.push(Util.base64ToHex(userChannel)); list.push(userChannel);
list.sort(); list.sort();
return list; return list;
@ -115,7 +116,7 @@ define([
// because of the expiration time // because of the expiration time
if ((data.owners && data.owners.length && data.owners.indexOf(edPublic) === -1) || if ((data.owners && data.owners.length && data.owners.indexOf(edPublic) === -1) ||
(data.expire && data.expire < (+new Date()))) { (data.expire && data.expire < (+new Date()))) {
list.push(Hash.hrefToHexChannelId(data.href)); list.push(data.channel);
} }
}); });
return list; return list;
@ -303,7 +304,7 @@ define([
Store.getFileSize = function (data, cb) { Store.getFileSize = function (data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); } if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var channelId = Hash.hrefToHexChannelId(data.href); var channelId = Hash.hrefToHexChannelId(data.href, data.password);
store.anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) { store.anon_rpc.send("GET_FILE_SIZE", channelId, function (e, response) {
if (e) { return void cb({error: e}); } if (e) { return void cb({error: e}); }
if (response && response.length && typeof(response[0]) === 'number') { if (response && response.length && typeof(response[0]) === 'number') {
@ -316,7 +317,7 @@ define([
Store.isNewChannel = function (data, cb) { Store.isNewChannel = function (data, cb) {
if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); } if (!store.anon_rpc) { return void cb({error: 'ANON_RPC_NOT_READY'}); }
var channelId = Hash.hrefToHexChannelId(data.href); var channelId = Hash.hrefToHexChannelId(data.href, data.password);
store.anon_rpc.send("IS_NEW_CHANNEL", channelId, function (e, response) { store.anon_rpc.send("IS_NEW_CHANNEL", channelId, function (e, response) {
if (e) { return void cb({error: e}); } if (e) { return void cb({error: e}); }
if (response && response.length && typeof(response[0]) === 'boolean') { if (response && response.length && typeof(response[0]) === 'boolean') {
@ -416,6 +417,8 @@ define([
var pad = makePad(data.href, data.title); var pad = makePad(data.href, data.title);
if (data.owners) { pad.owners = data.owners; } if (data.owners) { pad.owners = data.owners; }
if (data.expire) { pad.expire = data.expire; } if (data.expire) { pad.expire = data.expire; }
if (data.password) { pad.password = data.password; }
if (data.channel) { pad.channel = data.channel; }
store.userObject.pushData(pad, function (e, id) { store.userObject.pushData(pad, function (e, id) {
if (e) { return void cb({error: "Error while adding a template:"+ e}); } if (e) { return void cb({error: "Error while adding a template:"+ e}); }
var path = data.path || ['root']; var path = data.path || ['root'];
@ -433,14 +436,16 @@ define([
// Push channels owned by someone else or channel that should have expired // Push channels owned by someone else or channel that should have expired
// because of the expiration time // because of the expiration time
if (data.owners && data.owners.length === 1 && data.owners.indexOf(edPublic) !== -1) { if (data.owners && data.owners.length === 1 && data.owners.indexOf(edPublic) !== -1) {
list.push(Hash.hrefToHexChannelId(data.href)); list.push(data.channel);
} }
}); });
if (store.proxy.todo) { if (store.proxy.todo) {
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo)); // No password for todo
list.push(Hash.hrefToHexChannelId('/todo/#' + store.proxy.todo, null));
} }
if (store.proxy.profile && store.proxy.profile.edit) { if (store.proxy.profile && store.proxy.profile.edit) {
list.push(Hash.hrefToHexChannelId('/profile/#' + store.proxy.profile.edit)); // No password for profile
list.push(Hash.hrefToHexChannelId('/profile/#' + store.proxy.profile.edit, null));
} }
return list; return list;
}; };
@ -461,6 +466,7 @@ define([
Store.deleteAccount = function (data, cb) { Store.deleteAccount = function (data, cb) {
var edPublic = store.proxy.edPublic; var edPublic = store.proxy.edPublic;
// No password for drive
var secret = Hash.getSecrets('drive', storeHash); var secret = Hash.getSecrets('drive', storeHash);
Store.anonRpcMsg({ Store.anonRpcMsg({
msg: 'GET_METADATA', msg: 'GET_METADATA',
@ -524,25 +530,19 @@ define([
*/ */
Store.createReadme = function (data, cb) { Store.createReadme = function (data, cb) {
require(['/common/cryptget.js'], function (Crypt) { require(['/common/cryptget.js'], function (Crypt) {
var hash = Hash.createRandomHash(); var hash = Hash.createRandomHash('pad');
Crypt.put(hash, data.driveReadme, function (e) { Crypt.put(hash, data.driveReadme, function (e) {
if (e) { if (e) {
return void cb({ error: "Error while creating the default pad:"+ e}); return void cb({ error: "Error while creating the default pad:"+ e});
} }
var href = '/pad/#' + hash; var href = '/pad/#' + hash;
var channel = Hash.hrefToHexChannelId(href, null);
var fileData = { var fileData = {
href: href, href: href,
channel: channel,
title: data.driveReadmeTitle, title: data.driveReadmeTitle,
atime: +new Date(),
ctime: +new Date()
}; };
store.userObject.pushData(fileData, function (e, id) { Store.addPad(fileData, cb);
if (e) {
return void cb({ error: "Error while creating the default pad:"+ e});
}
store.userObject.add(id);
onSync(cb);
});
}); });
}); });
}; };
@ -679,17 +679,18 @@ define([
Store.setPadTitle = function (data, cb) { Store.setPadTitle = function (data, cb) {
var title = data.title; var title = data.title;
var href = data.href; var href = data.href;
var channel = data.channel;
var p = Hash.parsePadUrl(href); var p = Hash.parsePadUrl(href);
var h = p.hashData; var h = p.hashData;
if (AppConfig.disableAnonymousStore && !store.loggedIn) { return void cb(); } if (AppConfig.disableAnonymousStore && !store.loggedIn) { return void cb(); }
var owners; var owners;
if (Store.channel && Store.channel.wc && Util.base64ToHex(h.channel) === Store.channel.wc.id) { if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
owners = Store.channel.data.owners || undefined; owners = Store.channel.data.owners || undefined;
} }
var expire; var expire;
if (Store.channel && Store.channel.wc && Util.base64ToHex(h.channel) === Store.channel.wc.id) { if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
expire = +Store.channel.data.expire || undefined; expire = +Store.channel.data.expire || undefined;
} }
@ -712,13 +713,13 @@ define([
// Different types, proceed to the next one // Different types, proceed to the next one
// No hash data: corrupted pad? // No hash data: corrupted pad?
if (p.type !== p2.type || !h2) { continue; } if (p.type !== p2.type || !h2) { continue; }
// Different channel: continue
if (pad.channel !== channel) { continue; }
var shouldUpdate = p.hash.replace(/\/$/, '') === p2.hash.replace(/\/$/, ''); var shouldUpdate = p.hash.replace(/\/$/, '') === p2.hash.replace(/\/$/, '');
// If the hash is different but represents the same channel, check if weaker or stronger // If the hash is different but represents the same channel, check if weaker or stronger
if (!shouldUpdate && if (!shouldUpdate && h.version !== 0) {
h.version === 1 && h2.version === 1 &&
h.channel === h2.channel) {
// We had view & now we have edit, update // We had view & now we have edit, update
if (h2.mode === 'view' && h.mode === 'edit') { shouldUpdate = true; } if (h2.mode === 'view' && h.mode === 'edit') { shouldUpdate = true; }
// Same mode and we had present URL, update // Same mode and we had present URL, update
@ -755,10 +756,12 @@ define([
if (!contains) { if (!contains) {
Store.addPad({ Store.addPad({
href: href, href: href,
channel: channel,
title: title, title: title,
owners: owners, owners: owners,
expire: expire, expire: expire,
path: data.path || (store.data && store.data.initialPath) password: data.password,
path: data.path
}, cb); }, cb);
return; return;
} }
@ -798,10 +801,7 @@ define([
Store.getPadData = function (id, cb) { Store.getPadData = function (id, cb) {
cb(store.userObject.getFileData(id)); cb(store.userObject.getFileData(id));
}; };
Store.setInitialPath = function (path) {
if (!store.data) { return; }
store.data.initialPath = path;
};
// Messaging (manage friends from the userlist) // Messaging (manage friends from the userlist)
var getMessagingCfg = function () { var getMessagingCfg = function () {
@ -833,9 +833,9 @@ define([
var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {}; var allPads = Util.find(store.proxy, ['drive', 'filesData']) || {};
// If we have a stronger version in drive, add it and add a redirect button // If we have a stronger version in drive, add it and add a redirect button
var stronger = Hash.findStronger(data.href, allPads); var stronger = Hash.findStronger(data.href, data.channel, allPads);
if (stronger) { if (stronger) {
var parsed2 = Hash.parsePadUrl(stronger); var parsed2 = Hash.parsePadUrl(stronger.href);
return void cb(parsed2.hash); return void cb(parsed2.hash);
} }
cb(); cb();
@ -1051,11 +1051,24 @@ define([
postMessage("DRIVE_LOG", msg); postMessage("DRIVE_LOG", msg);
} }
}); });
var todo = function () { nThen(function (waitFor) {
postMessage('LOADING_DRIVE', {
state: 2
});
userObject.migrate(waitFor());
}).nThen(function (waitFor) {
Migrate(proxy, waitFor(), function (version, progress) {
postMessage('LOADING_DRIVE', {
state: 2,
progress: progress
});
});
}).nThen(function () {
postMessage('LOADING_DRIVE', {
state: 3
});
userObject.fixFiles(); userObject.fixFiles();
Migrate(proxy);
var requestLogin = function () { var requestLogin = function () {
postMessage("REQUEST_LOGIN"); postMessage("REQUEST_LOGIN");
}; };
@ -1118,16 +1131,16 @@ define([
proxy.on('change', [Constants.tokenKey], function () { proxy.on('change', [Constants.tokenKey], function () {
postMessage("UPDATE_TOKEN", { token: proxy[Constants.tokenKey] }); postMessage("UPDATE_TOKEN", { token: proxy[Constants.tokenKey] });
}); });
}; });
userObject.migrate(todo);
}; };
var connect = function (data, cb) { var connect = function (data, cb) {
var hash = data.userHash || data.anonHash || Hash.createRandomHash(); var hash = data.userHash || data.anonHash || Hash.createRandomHash('drive');
storeHash = hash; storeHash = hash;
if (!hash) { if (!hash) {
throw new Error('[Store.init] Unable to find or create a drive hash. Aborting...'); throw new Error('[Store.init] Unable to find or create a drive hash. Aborting...');
} }
// No password for drive
var secret = Hash.getSecrets('drive', hash); var secret = Hash.getSecrets('drive', hash);
var listmapConfig = { var listmapConfig = {
data: {}, data: {},
@ -1150,7 +1163,7 @@ define([
store.realtime = info.realtime; store.realtime = info.realtime;
store.network = info.network; store.network = info.network;
if (!data.userHash) { if (!data.userHash) {
returned.anonHash = Hash.getEditHashFromKeys(info.channel, secret.keys); returned.anonHash = Hash.getEditHashFromKeys(secret);
} }
}).on('ready', function () { }).on('ready', function () {
if (store.userObject) { return; } // the store is already ready, it is a reconnection if (store.userObject) { return; } // the store is already ready, it is a reconnection
@ -1161,6 +1174,7 @@ define([
&& !drive['filesData']) { && !drive['filesData']) {
drive[Constants.oldStorageKey] = []; drive[Constants.oldStorageKey] = [];
} }
postMessage('LOADING_DRIVE', { state: 1 });
// Drive already exist: return the existing drive, don't load data from legacy store // Drive already exist: return the existing drive, don't load data from legacy store
onReady(returned, cb); onReady(returned, cb);
}) })

@ -108,7 +108,7 @@ define([
// Make sure we have an FS_hash in localStorage before reloading all the tabs // Make sure we have an FS_hash in localStorage before reloading all the tabs
// so that we don't end up with tabs using different anon hashes // so that we don't end up with tabs using different anon hashes
if (!LocalStore.getFSHash()) { if (!LocalStore.getFSHash()) {
LocalStore.setFSHash(Hash.createRandomHash()); LocalStore.setFSHash(Hash.createRandomHash('drive'));
} }
eraseTempSessionValues(); eraseTempSessionValues();

@ -120,9 +120,6 @@ define([
case 'GET_PAD_DATA': { case 'GET_PAD_DATA': {
Store.getPadData(data, cb); break; Store.getPadData(data, cb); break;
} }
case 'SET_INITIAL_PATH': {
Store.setInitialPath(data); break;
}
case 'GET_STRONGER_HASH': { case 'GET_STRONGER_HASH': {
Store.getStrongerHash(data, cb); break; Store.getStrongerHash(data, cb); break;
} }

@ -51,14 +51,28 @@ define([
var b64Key = Nacl.util.encodeBase64(key); var b64Key = Nacl.util.encodeBase64(key);
var hash = Hash.getFileHashFromKeys(id, b64Key); var secret = {
version: 1,
channel: id,
keys: {
fileKeyStr: b64Key
}
};
var hash = Hash.getFileHashFromKeys(secret);
var href = '/file/#' + hash; var href = '/file/#' + hash;
var title = metadata.name; var title = metadata.name;
if (noStore) { return void onComplete(href); } if (noStore) { return void onComplete(href); }
common.setPadTitle(title || "", href, path, function (err) { // PASSWORD_FILES
var data = {
title: title || "",
href: href,
path: path,
channel: id
};
common.setPadTitle(data, function (err) {
if (err) { return void console.error(err); } if (err) { return void console.error(err); }
onComplete(href); onComplete(href);
common.setPadAttribute('fileType', metadata.type, null, href); common.setPadAttribute('fileType', metadata.type, null, href);

@ -75,7 +75,7 @@ define([
return void todo(); return void todo();
} }
if (!pinPads) { return; } if (!pinPads) { return; }
pinPads([Hash.hrefToHexChannelId(data.href)], function (obj) { pinPads([data.channel], function (obj) {
if (obj && obj.error) { return void cb(obj.error); } if (obj && obj.error) { return void cb(obj.error); }
todo(); todo();
}); });
@ -98,7 +98,7 @@ define([
exp.getFiles([FILES_DATA]).forEach(function (id) { exp.getFiles([FILES_DATA]).forEach(function (id) {
if (filesList.indexOf(id) === -1) { if (filesList.indexOf(id) === -1) {
var fd = exp.getFileData(id); var fd = exp.getFileData(id);
var channelId = fd && fd.href && Hash.hrefToHexChannelId(fd.href); var channelId = fd.channel;
// If trying to remove an owned pad, remove it from server also // If trying to remove an owned pad, remove it from server also
if (!isOwnPadRemoved && if (!isOwnPadRemoved &&
fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) { fd.owners && fd.owners.indexOf(edPublic) !== -1 && channelId) {
@ -552,32 +552,52 @@ define([
for (var id in fd) { for (var id in fd) {
id = Number(id); id = Number(id);
var el = fd[id]; var el = fd[id];
// Clean corrupted data
if (!el || typeof(el) !== "object") { if (!el || typeof(el) !== "object") {
debug("An element in filesData was not an object.", el); debug("An element in filesData was not an object.", el);
toClean.push(id); toClean.push(id);
continue; continue;
} }
// Clean missing href
if (!el.href) { if (!el.href) {
debug("Removing an element in filesData with a missing href.", el); debug("Removing an element in filesData with a missing href.", el);
toClean.push(id); toClean.push(id);
continue; continue;
} }
if (/^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
if (!el.ctime) { el.ctime = el.atime; }
var parsed = Hash.parsePadUrl(el.href); var parsed = Hash.parsePadUrl(el.href);
if (!el.title) { el.title = Hash.getDefaultName(parsed); } // Clean invalid hash
if (!parsed.hash) { if (!parsed.hash) {
debug("Removing an element in filesData with a invalid href.", el); debug("Removing an element in filesData with a invalid href.", el);
toClean.push(id); toClean.push(id);
continue; continue;
} }
// Clean invalid type
if (!parsed.type) { if (!parsed.type) {
debug("Removing an element in filesData with a invalid type.", el); debug("Removing an element in filesData with a invalid type.", el);
toClean.push(id); toClean.push(id);
continue; continue;
} }
// Fix href
if (/^https*:\/\//.test(el.href)) { el.href = Hash.getRelativeHref(el.href); }
// Fix creation time
if (!el.ctime) { el.ctime = el.atime; }
// Fix title
if (!el.title) { el.title = Hash.getDefaultName(parsed); }
// Fix channel
if (!el.channel) {
if (parsed.hashData && parsed.hashData.type === "file") {
// PASSWORD_FILES
el.channel = Util.base64ToHex(parsed.hashData.channel);
} else {
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
el.channel = secret.channel;
}
console.log('Adding missing channel in filesData ', el.channel);
}
if ((loggedIn || config.testMode) && rootFiles.indexOf(id) === -1) { if ((loggedIn || config.testMode) && rootFiles.indexOf(id) === -1) {
debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el); debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el);
var newName = Hash.createChannelId(); var newName = Hash.createChannelId();

@ -279,6 +279,8 @@ define([
var newContentStr = cpNfInner.chainpad.getUserDoc(); var newContentStr = cpNfInner.chainpad.getUserDoc();
if (state === STATE.DELETED) { return; } if (state === STATE.DELETED) { return; }
//UI.updateLoadingProgress({ state: -1 }, false);
var newPad = false; var newPad = false;
if (newContentStr === '') { newPad = true; } if (newContentStr === '') { newPad = true; }
@ -315,8 +317,7 @@ define([
privateDat.availableHashes.viewHash; privateDat.availableHashes.viewHash;
var href = privateDat.pathname + '#' + hash; var href = privateDat.pathname + '#' + hash;
if (AppConfig.textAnalyzer && textContentGetter) { if (AppConfig.textAnalyzer && textContentGetter) {
var channelId = Hash.hrefToHexChannelId(href); AppConfig.textAnalyzer(textContentGetter, privateDat.channel);
AppConfig.textAnalyzer(textContentGetter, channelId);
} }
if (options.thumbnail && privateDat.thumbnails) { if (options.thumbnail && privateDat.thumbnails) {
@ -432,6 +433,9 @@ define([
nThen(function (waitFor) { nThen(function (waitFor) {
UI.addLoadingScreen(); UI.addLoadingScreen();
SFCommon.create(waitFor(function (c) { common = c; })); SFCommon.create(waitFor(function (c) { common = c; }));
/*UI.updateLoadingProgress({
state: 1
}, false);*/
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
common.getSframeChannel().onReady(waitFor()); common.getSframeChannel().onReady(waitFor());
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
@ -443,7 +447,7 @@ define([
patchTransformer: options.patchTransformer || ChainPad.SmartJSONTransformer, patchTransformer: options.patchTransformer || ChainPad.SmartJSONTransformer,
// cryptpad debug logging (default is 1) // cryptpad debug logging (default is 1)
logLevel: 2, logLevel: 1,
validateContent: options.validateContent || function (content) { validateContent: options.validateContent || function (content) {
try { try {
JSON.parse(content); JSON.parse(content);
@ -457,7 +461,12 @@ define([
}, },
onRemote: onRemote, onRemote: onRemote,
onLocal: onLocal, onLocal: onLocal,
onInit: function () { stateChange(STATE.INITIALIZING); }, onInit: function () {
/*UI.updateLoadingProgress({
state: 2
}, false);*/
stateChange(STATE.INITIALIZING);
},
onReady: function () { evStart.reg(onReady); }, onReady: function () { evStart.reg(onReady); },
onConnectionChange: onConnectionChange, onConnectionChange: onConnectionChange,
onError: onError onError: onError

@ -332,6 +332,7 @@ define([
//var cursor = editor.getCursor(); //var cursor = editor.getCursor();
//var cleanName = data.name.replace(/[\[\]]/g, ''); //var cleanName = data.name.replace(/[\[\]]/g, '');
//var text = '!['+cleanName+']('+data.url+')'; //var text = '!['+cleanName+']('+data.url+')';
// PASSWORD_FILES
var parsed = Hash.parsePadUrl(data.url); var parsed = Hash.parsePadUrl(data.url);
var hexFileName = Util.base64ToHex(parsed.hashData.channel); var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;

@ -24,6 +24,8 @@ define([
var Utils = {}; var Utils = {};
var AppConfig; var AppConfig;
var Test; var Test;
var password;
var initialPathInDrive;
nThen(function (waitFor) { nThen(function (waitFor) {
// Load #2, the loading screen is up so grab whatever you need... // Load #2, the loading screen is up so grab whatever you need...
@ -88,7 +90,16 @@ define([
SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) { SFrameChannel.create($('#sbox-iframe')[0].contentWindow, waitFor(function (sfc) {
sframeChan = sfc; sframeChan = sfc;
}), false, { cache: cache, localStore: localStore, language: Cryptpad.getLanguage() }); }), false, { cache: cache, localStore: localStore, language: Cryptpad.getLanguage() });
Cryptpad.ready(waitFor(), { Cryptpad.loading.onDriveEvent.reg(function (data) {
if (sframeChan) { sframeChan.event('EV_LOADING_INFO', data); }
});
Cryptpad.ready(waitFor(function () {
if (sframeChan) {
sframeChan.event('EV_LOADING_INFO', {
state: -1
});
}
}), {
messenger: cfg.messaging, messenger: cfg.messaging,
driveEvents: cfg.driveEvents driveEvents: cfg.driveEvents
}); });
@ -113,6 +124,7 @@ define([
if (cfg.getSecrets) { if (cfg.getSecrets) {
var w = waitFor(); var w = waitFor();
// No password for drive, profile and todo
cfg.getSecrets(Cryptpad, Utils, waitFor(function (err, s) { cfg.getSecrets(Cryptpad, Utils, waitFor(function (err, s) {
secret = s; secret = s;
Cryptpad.getShareHashes(secret, function (err, h) { Cryptpad.getShareHashes(secret, function (err, h) {
@ -121,19 +133,54 @@ define([
}); });
})); }));
} else { } else {
secret = Utils.Hash.getSecrets(); var parsed = Utils.Hash.parsePadUrl(window.location.href);
if (!secret.channel) { var todo = function () {
// New pad: create a new random channel id secret = Utils.Hash.getSecrets(parsed.type, void 0, password);
secret.channel = Utils.Hash.createChannelId(); Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; }));
};
// Prompt the password here if we have a hash containing /p/
// or get it from the pad attributes
var needPassword = parsed.hashData && parsed.hashData.password;
if (needPassword) {
Cryptpad.getPadAttribute('password', waitFor(function (err, val) {
if (val) {
// We already know the password, use it!
password = val;
todo();
} else {
// Ask for the password and check if the pad exists
// If the pad doesn't exist, it means the password is oncorrect
// or the pad has been deleted
var correctPassword = waitFor();
sframeChan.on('Q_PAD_PASSWORD_VALUE', function (data, cb) {
password = data;
Cryptpad.isNewChannel(window.location.href, password, function (e, isNew) {
if (Boolean(isNew)) {
// Ask again in the inner iframe
// We should receive a new Q_PAD_PASSWORD_VALUE
cb(false);
} else {
todo();
correctPassword();
cb(true);
}
});
});
sframeChan.event("EV_PAD_PASSWORD");
}
}), parsed.getUrl());
return;
} }
Cryptpad.getShareHashes(secret, waitFor(function (err, h) { hashes = h; })); // If no password, continue...
todo();
} }
}).nThen(function (waitFor) { }).nThen(function (waitFor) {
// Check if the pad exists on server // Check if the pad exists on server
if (!window.location.hash) { isNewFile = true; return; } if (!window.location.hash) { isNewFile = true; return; }
if (realtime) { if (realtime) {
Cryptpad.isNewChannel(window.location.href, waitFor(function (e, isNew) { Cryptpad.isNewChannel(window.location.href, password, waitFor(function (e, isNew) {
if (e) { return console.error(e); } if (e) { return console.error(e); }
isNewFile = Boolean(isNew); isNewFile = Boolean(isNew);
})); }));
@ -188,7 +235,9 @@ define([
}, },
isNewFile: isNewFile, isNewFile: isNewFile,
isDeleted: isNewFile && window.location.hash.length > 0, isDeleted: isNewFile && window.location.hash.length > 0,
forceCreationScreen: forceCreationScreen forceCreationScreen: forceCreationScreen,
password: password,
channel: secret.channel
}; };
for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; } for (var k in additionalPriv) { metaObj.priv[k] = additionalPriv[k]; }
@ -256,7 +305,13 @@ define([
sframeChan.on('Q_SET_PAD_TITLE_IN_DRIVE', function (newTitle, cb) { sframeChan.on('Q_SET_PAD_TITLE_IN_DRIVE', function (newTitle, cb) {
currentTitle = newTitle; currentTitle = newTitle;
setDocumentTitle(); setDocumentTitle();
Cryptpad.setPadTitle(newTitle, undefined, undefined, function (err) { var data = {
password: password,
title: newTitle,
channel: secret.channel,
path: initialPathInDrive // Where to store the pad if we don't have it in our drive
};
Cryptpad.setPadTitle(data, function (err) {
cb(err); cb(err);
}); });
}); });
@ -473,9 +528,9 @@ define([
cb(templates.length > 0); cb(templates.length > 0);
}); });
}); });
var getKey = function (href) { var getKey = function (href, channel) {
var parsed = Utils.Hash.parsePadUrl(href); var parsed = Utils.Hash.parsePadUrl(href);
return 'thumbnail-' + parsed.type + '-' + parsed.hashData.channel; return 'thumbnail-' + parsed.type + '-' + channel;
}; };
sframeChan.on('Q_CREATE_TEMPLATES', function (type, cb) { sframeChan.on('Q_CREATE_TEMPLATES', function (type, cb) {
Cryptpad.getSecureFilesList({ Cryptpad.getSecureFilesList({
@ -488,7 +543,7 @@ define([
var res = []; var res = [];
nThen(function (waitFor) { nThen(function (waitFor) {
Object.keys(data).map(function (el) { Object.keys(data).map(function (el) {
var k = getKey(data[el].href); var k = getKey(data[el].href, data[el].channel);
Utils.LocalStore.getThumbnail(k, waitFor(function (e, thumb) { Utils.LocalStore.getThumbnail(k, waitFor(function (e, thumb) {
res.push({ res.push({
id: el, id: el,
@ -635,7 +690,7 @@ define([
isNewHash: isNewHash, isNewHash: isNewHash,
readOnly: readOnly, readOnly: readOnly,
crypto: Crypto.createEncryptor(secret.keys), crypto: Crypto.createEncryptor(secret.keys),
onConnect: function (wc) { onConnect: function () {
if (window.location.hash && window.location.hash !== '#') { if (window.location.hash && window.location.hash !== '#') {
window.location = parsed.getUrl({ window.location = parsed.getUrl({
present: parsed.hashData.present, present: parsed.hashData.present,
@ -644,7 +699,7 @@ define([
return; return;
} }
if (readOnly || cfg.noHash) { return; } if (readOnly || cfg.noHash) { return; }
replaceHash(Utils.Hash.getEditHashFromKeys(wc, secret.keys)); replaceHash(Utils.Hash.getEditHashFromKeys(secret));
} }
}; };
@ -671,8 +726,9 @@ define([
sframeChan.on('Q_CREATE_PAD', function (data, cb) { sframeChan.on('Q_CREATE_PAD', function (data, cb) {
if (!isNewFile || rtStarted) { return; } if (!isNewFile || rtStarted) { return; }
// Create a new hash // Create a new hash
var newHash = Utils.Hash.createRandomHash(); password = data.password;
secret = Utils.Hash.getSecrets(parsed.type, newHash); var newHash = Utils.Hash.createRandomHash(parsed.type, password);
secret = Utils.Hash.getSecrets(parsed.type, newHash, password);
// Update the hash in the address bar // Update the hash in the address bar
var ohc = window.onhashchange; var ohc = window.onhashchange;
@ -684,7 +740,7 @@ define([
// Update metadata values and send new metadata inside // Update metadata values and send new metadata inside
parsed = Utils.Hash.parsePadUrl(window.location.href); parsed = Utils.Hash.parsePadUrl(window.location.href);
defaultTitle = Utils.Hash.getDefaultName(parsed); defaultTitle = Utils.Hash.getDefaultName(parsed);
hashes = Utils.Hash.getHashes(secret.channel, secret); hashes = Utils.Hash.getHashes(secret);
readOnly = false; readOnly = false;
updateMeta(); updateMeta();
@ -698,7 +754,7 @@ define([
nThen(function(waitFor) { nThen(function(waitFor) {
if (data.templateId) { if (data.templateId) {
if (data.templateId === -1) { if (data.templateId === -1) {
Cryptpad.setInitialPath(['template']); initialPathInDrive = ['template'];
return; return;
} }
Cryptpad.getPadData(data.templateId, waitFor(function (err, d) { Cryptpad.getPadData(data.templateId, waitFor(function (err, d) {

@ -113,6 +113,7 @@ define([
return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>'; return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>';
}; };
funcs.getMediatagFromHref = function (href) { funcs.getMediatagFromHref = function (href) {
// PASSWORD_FILES
var parsed = Hash.parsePadUrl(href); var parsed = Hash.parsePadUrl(href);
var secret = Hash.getSecrets('file', parsed.hash); var secret = Hash.getSecrets('file', parsed.hash);
var data = ctx.metadataMgr.getPrivateData(); var data = ctx.metadataMgr.getPrivateData();
@ -126,8 +127,7 @@ define([
} }
return; return;
}; };
funcs.getFileSize = function (href, cb) { funcs.getFileSize = function (channelId, cb) {
var channelId = Hash.hrefToHexChannelId(href);
funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) { funcs.sendAnonRpcMsg("GET_FILE_SIZE", channelId, function (data) {
if (!data) { return void cb("No response"); } if (!data) { return void cb("No response"); }
if (data.error) { return void cb(data.error); } if (data.error) { return void cb(data.error); }
@ -197,6 +197,7 @@ define([
ctx.sframeChan.query("Q_CREATE_PAD", { ctx.sframeChan.query("Q_CREATE_PAD", {
owned: cfg.owned, owned: cfg.owned,
expire: cfg.expire, expire: cfg.expire,
password: cfg.password,
template: cfg.template, template: cfg.template,
templateId: cfg.templateId templateId: cfg.templateId
}, cb); }, cb);
@ -428,6 +429,14 @@ define([
UI.log(data.logText); UI.log(data.logText);
}); });
ctx.sframeChan.on("EV_PAD_PASSWORD", function () {
UIElements.displayPasswordPrompt(funcs);
});
ctx.sframeChan.on('EV_LOADING_INFO', function (data) {
UI.updateLoadingProgress(data, true);
});
ctx.metadataMgr.onReady(waitFor()); ctx.metadataMgr.onReady(waitFor());
}).nThen(function () { }).nThen(function () {
try { try {

@ -230,4 +230,11 @@ define({
// Critical error outside the iframe during loading screen // Critical error outside the iframe during loading screen
'EV_LOADING_ERROR': true, 'EV_LOADING_ERROR': true,
// Ask for the pad password when a pad is protected
'EV_PAD_PASSWORD': true,
'Q_PAD_PASSWORD_VALUE': true,
// Loading events to display in the loading screen
'EV_LOADING_INFO': true,
}); });

@ -576,9 +576,7 @@ define([
if (Common.isLoggedIn()) { return; } if (Common.isLoggedIn()) { return; }
var pd = config.metadataMgr.getPrivateData(); var pd = config.metadataMgr.getPrivateData();
var o = pd.origin; var o = pd.origin;
var hashes = pd.availableHashes; var cid = pd.channel;
var url = pd.origin + pd.pathname + '#' + (hashes.editHash || hashes.viewHash);
var cid = Hash.hrefToHexChannelId(url);
Common.sendAnonRpcMsg('IS_CHANNEL_PINNED', cid, function (x) { Common.sendAnonRpcMsg('IS_CHANNEL_PINNED', cid, function (x) {
if (x.error || !Array.isArray(x.response)) { return void console.log(x); } if (x.error || !Array.isArray(x.response)) { return void console.log(x); }
if (x.response[0] === true) { if (x.response[0] === true) {

@ -384,11 +384,9 @@ define([
// Get drive ids of files from their channel ids // Get drive ids of files from their channel ids
exp.findChannels = function (channels) { exp.findChannels = function (channels) {
var allFilesList = files[FILES_DATA]; var allFilesList = files[FILES_DATA];
var channels64 = channels.slice().map(Util.hexToBase64);
return getFiles([FILES_DATA]).filter(function (k) { return getFiles([FILES_DATA]).filter(function (k) {
var data = allFilesList[k]; var data = allFilesList[k];
var parsed = Hash.parsePadUrl(data.href); return channels.indexOf(data.channel) !== -1;
return parsed.hashData && channels64.indexOf(parsed.hashData.channel) !== -1;
}); });
}; };

@ -1296,7 +1296,7 @@ define([
$span.attr('title', name); $span.attr('title', name);
var type = Messages.type[hrefData.type] || hrefData.type; var type = Messages.type[hrefData.type] || hrefData.type;
common.displayThumbnail(data.href, $span, function ($thumb) { common.displayThumbnail(data.href, data.channel, $span, function ($thumb) {
// Called only if the thumbnail exists // Called only if the thumbnail exists
// Remove the .hide() added by displayThumnail() because it hides the icon in // Remove the .hide() added by displayThumnail() because it hides the icon in
// list mode too // list mode too
@ -2653,9 +2653,9 @@ define([
if (parsed.hashData.type !== "pad") { return; } if (parsed.hashData.type !== "pad") { return; }
var i = data.href.indexOf('#') + 1; var i = data.href.indexOf('#') + 1;
var base = data.href.slice(0, i); var base = data.href.slice(0, i);
var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash); var hrefsecret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
if (!hrefsecret.keys) { return; } if (!hrefsecret.keys) { return; }
var viewHash = Hash.getViewHashFromKeys(hrefsecret.channel, hrefsecret.keys); var viewHash = Hash.getViewHashFromKeys(hrefsecret);
return base + viewHash; return base + viewHash;
}; };
@ -2720,24 +2720,6 @@ define([
$(window).focus(); $(window).focus();
if (!res) { return; } if (!res) { return; }
filesOp.delete(pathsList, refresh); filesOp.delete(pathsList, refresh);
/*
// Try to delete each selected pad from server, and delete from drive if no error
var n = nThen(function () {});
pathsList.forEach(function (p) {
var el = filesOp.find(p);
var data = filesOp.getFileData(el);
var parsed = Hash.parsePadUrl(data.href);
var channel = Util.base64ToHex(parsed.hashData.channel);
n = n.nThen(function (waitFor) {
sframeChan.query('Q_REMOVE_OWNED_CHANNEL', channel,
waitFor(function (e) {
if (e) { return void console.error(e); }
filesOp.delete([p], function () {}, false, true);
}));
});
});
n.nThen(function () { refresh(); });
*/
}); });
}; };
$contextMenu.on("click", "a", function(e) { $contextMenu.on("click", "a", function(e) {

@ -39,6 +39,7 @@ define([
var getSecrets = function (Cryptpad, Utils, cb) { var getSecrets = function (Cryptpad, Utils, cb) {
var hash = window.location.hash.slice(1) || Utils.LocalStore.getUserHash() || var hash = window.location.hash.slice(1) || Utils.LocalStore.getUserHash() ||
Utils.LocalStore.getFSHash(); Utils.LocalStore.getFSHash();
// No password for drive
cb(null, Utils.Hash.getSecrets('drive', hash)); cb(null, Utils.Hash.getSecrets('drive', hash));
}; };
var addRpc = function (sframeChan, Cryptpad, Utils) { var addRpc = function (sframeChan, Cryptpad, Utils) {

@ -61,6 +61,7 @@ define([
if (!priv.filehash) { if (!priv.filehash) {
uploadMode = true; uploadMode = true;
} else { } else {
// PASSWORD_FILES
secret = Hash.getSecrets('file', priv.filehash); secret = Hash.getSecrets('file', priv.filehash);
if (!secret.keys) { throw new Error("You need a hash"); } if (!secret.keys) { throw new Error("You need a hash"); }
hexFileName = Util.base64ToHex(secret.channel); hexFileName = Util.base64ToHex(secret.channel);
@ -233,8 +234,7 @@ define([
if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); } if (typeof(sizeMb) === 'number' && sizeMb < 5) { return void onClick(); }
$dlform.find('#cp-app-file-dlfile, #cp-app-file-dlprogress').click(onClick); $dlform.find('#cp-app-file-dlfile, #cp-app-file-dlprogress').click(onClick);
}; };
var href = priv.origin + priv.pathname + priv.filehash; common.getFileSize(hexFileName, function (e, data) {
common.getFileSize(href, function (e, data) {
if (e) { if (e) {
return void UI.errorLoadingScreen(e); return void UI.errorLoadingScreen(e);
} }

@ -40,6 +40,7 @@ define([
var parsed = Hash.parsePadUrl(data.url); var parsed = Hash.parsePadUrl(data.url);
hideFileDialog(); hideFileDialog();
if (parsed.type === 'file') { if (parsed.type === 'file') {
// PASSWORD_FILES
var hexFileName = Util.base64ToHex(parsed.hashData.channel); var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
sframeChan.event("EV_FILE_PICKED", { sframeChan.event("EV_FILE_PICKED", {
@ -138,7 +139,7 @@ define([
}); });
// Add thumbnail if it exists // Add thumbnail if it exists
common.displayThumbnail(data.href, $span); common.displayThumbnail(data.href, data.channel, $span);
}); });
$input.focus(); $input.focus();
}; };

@ -552,6 +552,7 @@ define([
ckeditor: editor, ckeditor: editor,
body: $('body'), body: $('body'),
onUploaded: function (ev, data) { onUploaded: function (ev, data) {
// PASSWORD_FILES
var parsed = Hash.parsePadUrl(data.url); var parsed = Hash.parsePadUrl(data.url);
var hexFileName = Util.base64ToHex(parsed.hashData.channel); var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;

@ -2,7 +2,6 @@ define([
'jquery', 'jquery',
'/common/toolbar3.js', '/common/toolbar3.js',
'/common/common-util.js', '/common/common-util.js',
'/common/cryptget.js',
'/bower_components/nthen/index.js', '/bower_components/nthen/index.js',
'/common/sframe-common.js', '/common/sframe-common.js',
'/common/common-realtime.js', '/common/common-realtime.js',
@ -32,7 +31,6 @@ define([
$, $,
Toolbar, Toolbar,
Util, Util,
Cryptget,
nThen, nThen,
SFCommon, SFCommon,
CommonRealtime, CommonRealtime,

@ -40,6 +40,7 @@ define([
var Hash = Utils.Hash; var Hash = Utils.Hash;
// 1st case: visiting someone else's profile with hash in the URL // 1st case: visiting someone else's profile with hash in the URL
if (window.location.hash) { if (window.location.hash) {
// No password for profiles
return void cb(null, Hash.getSecrets('profile', window.location.hash.slice(1))); return void cb(null, Hash.getSecrets('profile', window.location.hash.slice(1)));
} }
var editHash; var editHash;
@ -50,6 +51,7 @@ define([
})); }));
}).nThen(function () { }).nThen(function () {
if (editHash) { if (editHash) {
// No password for profile
return void cb(null, Hash.getSecrets('profile', editHash)); return void cb(null, Hash.getSecrets('profile', editHash));
} }
// 3rd case: profile creation (create a new random hash, store it later if needed) // 3rd case: profile creation (create a new random hash, store it later if needed)
@ -58,7 +60,8 @@ define([
window.location.href = '/drive/'; window.location.href = '/drive/';
return void cb(); return void cb();
} }
var hash = Hash.createRandomHash(); // No password for profile
var hash = Hash.createRandomHash('profile');
var secret = Hash.getSecrets('profile', hash); var secret = Hash.getSecrets('profile', hash);
Cryptpad.pinPads([secret.channel], function (e) { Cryptpad.pinPads([secret.channel], function (e) {
if (e) { if (e) {
@ -69,8 +72,8 @@ define([
//return void UI.log(Messages._getKey('profile_error', [e])) // TODO //return void UI.log(Messages._getKey('profile_error', [e])) // TODO
} }
var profile = {}; var profile = {};
profile.edit = Utils.Hash.getEditHashFromKeys(secret.channel, secret.keys); profile.edit = Utils.Hash.getEditHashFromKeys(secret);
profile.view = Utils.Hash.getViewHashFromKeys(secret.channel, secret.keys); profile.view = Utils.Hash.getViewHashFromKeys(secret);
Cryptpad.setNewProfile(profile); Cryptpad.setNewProfile(profile);
}); });
cb(null, secret); cb(null, secret);
@ -79,7 +82,7 @@ define([
var addRpc = function (sframeChan, Cryptpad, Utils) { var addRpc = function (sframeChan, Cryptpad, Utils) {
// Adding a new avatar from the profile: pin it and store it in the object // Adding a new avatar from the profile: pin it and store it in the object
sframeChan.on('Q_PROFILE_AVATAR_ADD', function (data, cb) { sframeChan.on('Q_PROFILE_AVATAR_ADD', function (data, cb) {
var chanId = Utils.Hash.hrefToHexChannelId(data); var chanId = Utils.Hash.hrefToHexChannelId(data, null);
Cryptpad.pinPads([chanId], function (e) { Cryptpad.pinPads([chanId], function (e) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
Cryptpad.setAvatar(data, cb); Cryptpad.setAvatar(data, cb);
@ -87,7 +90,7 @@ define([
}); });
// Removing the avatar from the profile: unpin it // Removing the avatar from the profile: unpin it
sframeChan.on('Q_PROFILE_AVATAR_REMOVE', function (data, cb) { sframeChan.on('Q_PROFILE_AVATAR_REMOVE', function (data, cb) {
var chanId = Utils.Hash.hrefToHexChannelId(data); var chanId = Utils.Hash.hrefToHexChannelId(data, null);
Cryptpad.unpinPads([chanId], function () { Cryptpad.unpinPads([chanId], function () {
Cryptpad.setAvatar(undefined, cb); Cryptpad.setAvatar(undefined, cb);
}); });

@ -500,9 +500,7 @@ define([
dropArea: $('.CodeMirror'), dropArea: $('.CodeMirror'),
body: $('body'), body: $('body'),
onUploaded: function (ev, data) { onUploaded: function (ev, data) {
//var cursor = editor.getCursor(); // PASSWORD_FILES
//var cleanName = data.name.replace(/[\[\]]/g, '');
//var text = '!['+cleanName+']('+data.url+')';
var parsed = Hash.parsePadUrl(data.url); var parsed = Hash.parsePadUrl(data.url);
var hexFileName = Util.base64ToHex(parsed.hashData.channel); var hexFileName = Util.base64ToHex(parsed.hashData.channel);
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName; var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;

@ -38,7 +38,8 @@ define([
}).nThen(function (/*waitFor*/) { }).nThen(function (/*waitFor*/) {
var getSecrets = function (Cryptpad, Utils, cb) { var getSecrets = function (Cryptpad, Utils, cb) {
Cryptpad.getTodoHash(function (hash) { Cryptpad.getTodoHash(function (hash) {
var nHash = hash || Utils.Hash.createRandomHash(); // No password for todo
var nHash = hash || Utils.Hash.createRandomHash('todo');
if (!hash) { Cryptpad.setTodoHash(nHash); } if (!hash) { Cryptpad.setTodoHash(nHash); }
cb(null, Utils.Hash.getSecrets('todo', nHash)); cb(null, Utils.Hash.getSecrets('todo', nHash));
}); });

@ -389,7 +389,7 @@ define([
var D = Thumb.getResizedDimensions($canvas[0], 'pad'); var D = Thumb.getResizedDimensions($canvas[0], 'pad');
Thumb.fromCanvas($canvas[0], D, function (err, b64) { Thumb.fromCanvas($canvas[0], D, function (err, b64) {
oldThumbnailState = content; oldThumbnailState = content;
Thumb.setPadThumbnail(common, href, b64); Thumb.setPadThumbnail(common, href, privateDat.channel, b64);
}); });
}; };
window.setInterval(mkThumbnail, Thumb.UPDATE_INTERVAL); window.setInterval(mkThumbnail, Thumb.UPDATE_INTERVAL);

Loading…
Cancel
Save