Merge branch 'donkey' into staging
This commit is contained in:
@@ -11,6 +11,7 @@ define([
|
||||
var uint8ArrayToHex = Util.uint8ArrayToHex;
|
||||
var hexToBase64 = Util.hexToBase64;
|
||||
var base64ToHex = Util.base64ToHex;
|
||||
Hash.encodeBase64 = Nacl.util.encodeBase64;
|
||||
|
||||
// This implementation must match that on the server
|
||||
// it's used for a checksum
|
||||
@@ -59,25 +60,13 @@ define([
|
||||
return '/1/' + hexToBase64(secret.channel) + '/' +
|
||||
Crypto.b64RemoveSlashes(data.fileKeyStr) + '/';
|
||||
}
|
||||
if (version === 2) {
|
||||
if (!data.fileKeyStr) { return; }
|
||||
var pass = secret.password ? 'p/' : '';
|
||||
return '/2/' + secret.type + '/' + Crypto.b64RemoveSlashes(data.fileKeyStr) + '/' + pass;
|
||||
}
|
||||
};
|
||||
|
||||
// V1
|
||||
/*var getEditHashFromKeys = Hash.getEditHashFromKeys = function (chanKey, keys) {
|
||||
if (typeof keys === 'string') {
|
||||
return chanKey + keys;
|
||||
}
|
||||
if (!keys.editKeyStr) { return; }
|
||||
return '/1/edit/' + hexToBase64(chanKey) + '/'+Crypto.b64RemoveSlashes(keys.editKeyStr)+'/';
|
||||
};
|
||||
var getViewHashFromKeys = Hash.getViewHashFromKeys = function (chanKey, keys) {
|
||||
if (typeof keys === 'string') {
|
||||
return;
|
||||
}
|
||||
return '/1/view/' + hexToBase64(chanKey) + '/'+Crypto.b64RemoveSlashes(keys.viewKeyStr)+'/';
|
||||
};
|
||||
var getFileHashFromKeys = Hash.getFileHashFromKeys = function (fileKey, cryptKey) {
|
||||
return '/1/' + hexToBase64(fileKey) + '/' + Crypto.b64RemoveSlashes(cryptKey) + '/';
|
||||
};*/
|
||||
Hash.getUserHrefFromKeys = function (origin, username, pubkey) {
|
||||
return origin + '/user/#/1/' + username + '/' + pubkey.replace(/\//g, '-');
|
||||
};
|
||||
@@ -95,12 +84,22 @@ define([
|
||||
};
|
||||
|
||||
Hash.createRandomHash = function (type, password) {
|
||||
var cryptor = Crypto.createEditCryptor2(void 0, void 0, password);
|
||||
var cryptor;
|
||||
if (type === 'file') {
|
||||
cryptor = Crypto.createFileCryptor2(void 0, password);
|
||||
return getFileHashFromKeys({
|
||||
password: Boolean(password),
|
||||
version: 2,
|
||||
type: type,
|
||||
keys: cryptor
|
||||
});
|
||||
}
|
||||
cryptor = Crypto.createEditCryptor2(void 0, void 0, password);
|
||||
return getEditHashFromKeys({
|
||||
password: Boolean(password),
|
||||
version: 2,
|
||||
type: type,
|
||||
keys: { editKeyStr: cryptor.editKeyStr }
|
||||
keys: cryptor
|
||||
});
|
||||
};
|
||||
|
||||
@@ -113,6 +112,7 @@ Version 1
|
||||
|
||||
var parseTypeHash = Hash.parseTypeHash = function (type, hash) {
|
||||
if (!hash) { return; }
|
||||
var options;
|
||||
var parsed = {};
|
||||
var hashArr = fixDuplicateSlashes(hash).split('/');
|
||||
if (['media', 'file', 'user', 'invite'].indexOf(type) === -1) {
|
||||
@@ -125,7 +125,6 @@ Version 1
|
||||
parsed.version = 0;
|
||||
return parsed;
|
||||
}
|
||||
var options;
|
||||
if (hashArr[1] && hashArr[1] === '1') { // Version 1
|
||||
parsed.version = 1;
|
||||
parsed.mode = hashArr[2];
|
||||
@@ -175,6 +174,25 @@ Version 1
|
||||
parsed.key = hashArr[3].replace(/-/g, '/');
|
||||
return parsed;
|
||||
}
|
||||
if (hashArr[1] && hashArr[1] === '2') { // Version 2
|
||||
parsed.version = 2;
|
||||
parsed.app = hashArr[2];
|
||||
parsed.key = hashArr[3];
|
||||
|
||||
options = hashArr.slice(4);
|
||||
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, 4).join('/') + '/';
|
||||
if (parsed.password) { hash += 'p/'; }
|
||||
if (opts.embed) { hash += 'embed/'; }
|
||||
if (opts.present) { hash += 'present/'; }
|
||||
return hash;
|
||||
};
|
||||
return parsed;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
if (['user'].indexOf(type) !== -1) {
|
||||
@@ -309,11 +327,12 @@ Version 1
|
||||
}
|
||||
}
|
||||
} else if (parsed.type === "file") {
|
||||
// version 2 hashes are to be used for encrypted blobs
|
||||
secret.channel = parsed.channel;
|
||||
secret.keys = { fileKeyStr: parsed.key };
|
||||
secret.channel = base64ToHex(parsed.channel);
|
||||
secret.keys = {
|
||||
fileKeyStr: parsed.key,
|
||||
cryptKey: Nacl.util.decodeBase64(parsed.key)
|
||||
};
|
||||
} else if (parsed.type === "user") {
|
||||
// version 2 hashes are to be used for encrypted blobs
|
||||
throw new Error("User hashes can't be opened (yet)");
|
||||
}
|
||||
} else if (parsed.version === 2) {
|
||||
@@ -338,7 +357,12 @@ Version 1
|
||||
}
|
||||
}
|
||||
} else if (parsed.type === "file") {
|
||||
throw new Error("File hashes should be version 1");
|
||||
secret.keys = Crypto.createFileCryptor2(parsed.key, password);
|
||||
secret.channel = base64ToHex(secret.keys.chanId);
|
||||
secret.key = secret.keys.fileKeyStr;
|
||||
if (secret.channel.length !== 48 || secret.key.length !== 24) {
|
||||
throw new Error("The channel key and/or the encryption key is invalid");
|
||||
}
|
||||
} else if (parsed.type === "user") {
|
||||
throw new Error("User hashes can't be opened (yet)");
|
||||
}
|
||||
|
||||
@@ -425,7 +425,8 @@ define([
|
||||
cb = cb || function () {};
|
||||
opt = opt || {};
|
||||
|
||||
var input = dialog.textInput();
|
||||
var inputBlock = opt.password ? UI.passwordInput() : dialog.textInput();
|
||||
var input = opt.password ? $(inputBlock).find('input')[0] : inputBlock;
|
||||
input.value = typeof(def) === 'string'? def: '';
|
||||
|
||||
var message;
|
||||
@@ -441,7 +442,7 @@ define([
|
||||
var cancel = dialog.cancelButton(opt.cancel);
|
||||
var frame = dialog.frame([
|
||||
message,
|
||||
input,
|
||||
inputBlock,
|
||||
dialog.nav([ cancel, ok, ]),
|
||||
]);
|
||||
|
||||
|
||||
@@ -250,17 +250,15 @@ define([
|
||||
var k = getKey(parsed.type, channel);
|
||||
common.setThumbnail(k, b64, cb);
|
||||
};
|
||||
Thumb.displayThumbnail = function (common, href, channel, $container, cb) {
|
||||
Thumb.displayThumbnail = function (common, href, channel, password, $container, cb) {
|
||||
cb = cb || function () {};
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var k = getKey(parsed.type, channel);
|
||||
var whenNewThumb = function () {
|
||||
// PASSWORD_FILES
|
||||
var secret = Hash.getSecrets('file', parsed.hash);
|
||||
var hexFileName = Util.base64ToHex(secret.channel);
|
||||
var secret = Hash.getSecrets('file', parsed.hash, password);
|
||||
var hexFileName = channel;
|
||||
var src = Hash.getBlobPathFromHex(hexFileName);
|
||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
||||
var key = Nacl.util.decodeBase64(cryptKey);
|
||||
var key = secret.keys && secret.keys.cryptKey;
|
||||
FileCrypto.fetchDecryptedMetadata(src, key, function (e, metadata) {
|
||||
if (e) {
|
||||
if (e === 'XHR_ERROR') { return; }
|
||||
|
||||
@@ -162,7 +162,6 @@ define([
|
||||
$pwInput.val(data.password).click(function () {
|
||||
$pwInput[0].select();
|
||||
});
|
||||
$(password).find('.cp-checkmark').css('margin-bottom', '15px');
|
||||
$d.append(password);
|
||||
}
|
||||
|
||||
@@ -960,7 +959,7 @@ define([
|
||||
};
|
||||
};
|
||||
|
||||
var setHTML = function (e, html) {
|
||||
var setHTML = UIElements.setHTML = function (e, html) {
|
||||
e.innerHTML = html;
|
||||
return e;
|
||||
};
|
||||
@@ -1172,8 +1171,8 @@ define([
|
||||
// No password for avatars
|
||||
var secret = Hash.getSecrets('file', parsed.hash);
|
||||
if (secret.keys && secret.channel) {
|
||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
||||
var hexFileName = Util.base64ToHex(secret.channel);
|
||||
var hexFileName = secret.channel;
|
||||
var cryptKey = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
|
||||
var src = Hash.getBlobPathFromHex(hexFileName);
|
||||
Common.getFileSize(hexFileName, function (e, data) {
|
||||
if (e || !data) {
|
||||
|
||||
@@ -204,8 +204,8 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
common.uploadComplete = function (cb) {
|
||||
postMessage("UPLOAD_COMPLETE", null, function (obj) {
|
||||
common.uploadComplete = function (id, owned, cb) {
|
||||
postMessage("UPLOAD_COMPLETE", {id: id, owned: owned}, function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj.error); }
|
||||
cb(null, obj);
|
||||
});
|
||||
@@ -218,8 +218,8 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
common.uploadCancel = function (cb) {
|
||||
postMessage("UPLOAD_CANCEL", null, function (obj) {
|
||||
common.uploadCancel = function (size, cb) {
|
||||
postMessage("UPLOAD_CANCEL", {size: size}, function (obj) {
|
||||
if (obj && obj.error) { return void cb(obj.error); }
|
||||
cb(null, obj);
|
||||
});
|
||||
@@ -578,7 +578,6 @@ define([
|
||||
}
|
||||
var parsed = Hash.parsePadUrl(window.location.href);
|
||||
if (!parsed.type || !parsed.hashData) { return void cb('E_INVALID_HREF'); }
|
||||
if (parsed.type === 'file' && typeof(parsed.channel) === 'string') { secret.channel = Util.base64ToHex(secret.channel); }
|
||||
hashes = Hash.getHashes(secret);
|
||||
|
||||
if (secret.version === 0) {
|
||||
|
||||
@@ -41,11 +41,15 @@ define([
|
||||
};
|
||||
renderer.image = function (href, title, text) {
|
||||
if (href.slice(0,6) === '/file/') {
|
||||
// PASSWORD_FILES
|
||||
// DEPRECATED
|
||||
// Mediatag using markdown syntax should not be used anymore so they don't support
|
||||
// password-protected files
|
||||
console.log('DEPRECATED: mediatag using markdown syntax!');
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + parsed.hashData.key + '">';
|
||||
var secret = Hash.getSecrets('file', parsed.hash);
|
||||
var src = Hash.getBlobPathFromHex(secret.channel);
|
||||
var key = Hash.encodeBase64(secret.keys.cryptKey);
|
||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '"></media-tag>';
|
||||
if (mediaMap[src]) {
|
||||
mt += mediaMap[src];
|
||||
}
|
||||
|
||||
@@ -115,13 +115,8 @@ define([
|
||||
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;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ define([
|
||||
var profileChan = profile.edit ? Hash.hrefToHexChannelId('/profile/#' + profile.edit, null) : null;
|
||||
if (profileChan) { list.push(profileChan); }
|
||||
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar, null) : null;
|
||||
if (avatarChan) { list.push(Util.base64ToHex(avatarChan)); }
|
||||
if (avatarChan) { list.push(avatarChan); }
|
||||
}
|
||||
|
||||
if (store.proxy.friends) {
|
||||
@@ -232,7 +232,16 @@ define([
|
||||
|
||||
Store.uploadComplete = function (data, cb) {
|
||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
store.rpc.uploadComplete(function (err, res) {
|
||||
if (data.owned) {
|
||||
// Owned file
|
||||
store.rpc.ownedUploadComplete(data.id, function (err, res) {
|
||||
if (err) { return void cb({error:err}); }
|
||||
cb(res);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Normal upload
|
||||
store.rpc.uploadComplete(data.id, function (err, res) {
|
||||
if (err) { return void cb({error:err}); }
|
||||
cb(res);
|
||||
});
|
||||
@@ -248,7 +257,7 @@ define([
|
||||
|
||||
Store.uploadCancel = function (data, cb) {
|
||||
if (!store.rpc) { return void cb({error: 'RPC_NOT_READY'}); }
|
||||
store.rpc.uploadCancel(function (err, res) {
|
||||
store.rpc.uploadCancel(data.size, function (err, res) {
|
||||
if (err) { return void cb({error:err}); }
|
||||
cb(res);
|
||||
});
|
||||
@@ -678,6 +687,7 @@ define([
|
||||
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
|
||||
owners = Store.channel.data.owners || undefined;
|
||||
}
|
||||
|
||||
var expire;
|
||||
if (Store.channel && Store.channel.wc && channel === Store.channel.wc.id) {
|
||||
expire = +Store.channel.data.expire || undefined;
|
||||
@@ -726,7 +736,11 @@ define([
|
||||
contains = true;
|
||||
pad.atime = +new Date();
|
||||
pad.title = title;
|
||||
pad.owners = owners;
|
||||
if (owners || h.type !== "file") {
|
||||
// OWNED_FILES
|
||||
// Never remove owner for files
|
||||
pad.owners = owners;
|
||||
}
|
||||
pad.expire = expire;
|
||||
|
||||
// If the href is different, it means we have a stronger one
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
define([
|
||||
'/file/file-crypto.js',
|
||||
'/common/common-hash.js',
|
||||
'/bower_components/nthen/index.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function (FileCrypto, Hash) {
|
||||
], function (FileCrypto, Hash, nThen) {
|
||||
var Nacl = window.nacl;
|
||||
var module = {};
|
||||
|
||||
@@ -10,97 +11,122 @@ define([
|
||||
var u8 = file.blob; // This is not a blob but a uint8array
|
||||
var metadata = file.metadata;
|
||||
|
||||
var owned = file.isOwned;
|
||||
// XXX
|
||||
owned = true;
|
||||
|
||||
// if it exists, path contains the new pad location in the drive
|
||||
var path = file.path;
|
||||
|
||||
var key = Nacl.randomBytes(32);
|
||||
var next = FileCrypto.encrypt(u8, metadata, key);
|
||||
var password = file.password;
|
||||
var hash, secret, key, id, href;
|
||||
|
||||
var estimate = FileCrypto.computeEncryptedSize(u8.length, metadata);
|
||||
var getNewHash = function () {
|
||||
hash = Hash.createRandomHash('file', password);
|
||||
secret = Hash.getSecrets('file', hash, password);
|
||||
key = secret.keys.cryptKey;
|
||||
id = secret.channel;
|
||||
href = '/file/#' + hash;
|
||||
};
|
||||
|
||||
var sendChunk = function (box, cb) {
|
||||
var enc = Nacl.util.encodeBase64(box);
|
||||
common.uploadChunk(enc, function (e, msg) {
|
||||
cb(e, msg);
|
||||
var getValidHash = function (cb) {
|
||||
getNewHash();
|
||||
common.getFileSize(href, password, function (err, size) {
|
||||
if (err || typeof(size) !== "number") { throw new Error(err || "Invalid size!"); }
|
||||
if (size === 0) { return void cb(); }
|
||||
getValidHash();
|
||||
});
|
||||
};
|
||||
|
||||
var actual = 0;
|
||||
var again = function (err, box) {
|
||||
if (err) { throw new Error(err); }
|
||||
if (box) {
|
||||
actual += box.length;
|
||||
var progressValue = (actual / estimate * 100);
|
||||
updateProgress(progressValue);
|
||||
var edPublic;
|
||||
nThen(function (waitFor) {
|
||||
// Generate a hash and check if the resulting id is valid (not already used)
|
||||
getValidHash(waitFor());
|
||||
}).nThen(function (waitFor) {
|
||||
if (!owned) { return; }
|
||||
common.getMetadata(waitFor(function (err, m) {
|
||||
edPublic = m.priv.edPublic;
|
||||
metadata.owners = [edPublic];
|
||||
}));
|
||||
}).nThen(function () {
|
||||
var next = FileCrypto.encrypt(u8, metadata, key);
|
||||
|
||||
return void sendChunk(box, function (e) {
|
||||
if (e) { return console.error(e); }
|
||||
next(again);
|
||||
var estimate = FileCrypto.computeEncryptedSize(u8.length, metadata);
|
||||
|
||||
var sendChunk = function (box, cb) {
|
||||
var enc = Nacl.util.encodeBase64(box);
|
||||
common.uploadChunk(enc, function (e, msg) {
|
||||
cb(e, msg);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (actual !== estimate) {
|
||||
console.error('Estimated size does not match actual size');
|
||||
}
|
||||
var actual = 0;
|
||||
var again = function (err, box) {
|
||||
if (err) { throw new Error(err); }
|
||||
if (box) {
|
||||
actual += box.length;
|
||||
var progressValue = (actual / estimate * 100);
|
||||
updateProgress(progressValue);
|
||||
|
||||
// if not box then done
|
||||
common.uploadComplete(function (e, id) {
|
||||
if (e) { return void console.error(e); }
|
||||
var uri = ['', 'blob', id.slice(0,2), id].join('/');
|
||||
console.log("encrypted blob is now available as %s", uri);
|
||||
|
||||
var b64Key = Nacl.util.encodeBase64(key);
|
||||
|
||||
var secret = {
|
||||
version: 1,
|
||||
channel: id,
|
||||
keys: {
|
||||
fileKeyStr: b64Key
|
||||
}
|
||||
};
|
||||
var hash = Hash.getFileHashFromKeys(secret);
|
||||
var href = '/file/#' + hash;
|
||||
|
||||
var title = metadata.name;
|
||||
|
||||
if (noStore) { return void onComplete(href); }
|
||||
|
||||
// PASSWORD_FILES
|
||||
var data = {
|
||||
title: title || "",
|
||||
href: href,
|
||||
path: path,
|
||||
channel: id
|
||||
};
|
||||
common.setPadTitle(data, function (err) {
|
||||
if (err) { return void console.error(err); }
|
||||
onComplete(href);
|
||||
common.setPadAttribute('fileType', metadata.type, null, href);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
common.uploadStatus(estimate, function (e, pending) {
|
||||
if (e) {
|
||||
console.error(e);
|
||||
onError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
return void onPending(function () {
|
||||
// if the user wants to cancel the pending upload to execute that one
|
||||
common.uploadCancel(function (e, res) {
|
||||
if (e) {
|
||||
return void console.error(e);
|
||||
}
|
||||
console.log(res);
|
||||
return void sendChunk(box, function (e) {
|
||||
if (e) { return console.error(e); }
|
||||
next(again);
|
||||
});
|
||||
}
|
||||
|
||||
if (actual !== estimate) {
|
||||
console.error('Estimated size does not match actual size');
|
||||
}
|
||||
|
||||
// if not box then done
|
||||
common.uploadComplete(id, owned, function (e) {
|
||||
if (e) { return void console.error(e); }
|
||||
var uri = ['', 'blob', id.slice(0,2), id].join('/');
|
||||
console.log("encrypted blob is now available as %s", uri);
|
||||
|
||||
|
||||
var title = metadata.name;
|
||||
|
||||
if (noStore) { return void onComplete(href); }
|
||||
|
||||
var data = {
|
||||
title: title || "",
|
||||
href: href,
|
||||
path: path,
|
||||
password: password,
|
||||
channel: id
|
||||
};
|
||||
common.setPadTitle(data, function (err) {
|
||||
if (err) { return void console.error(err); }
|
||||
onComplete(href);
|
||||
common.setPadAttribute('fileType', metadata.type, null, href);
|
||||
common.setPadAttribute('owners', metadata.owners, null, href);
|
||||
});
|
||||
});
|
||||
}
|
||||
next(again);
|
||||
};
|
||||
|
||||
common.uploadStatus(estimate, function (e, pending) {
|
||||
if (e) {
|
||||
console.error(e);
|
||||
onError(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pending) {
|
||||
return void onPending(function () {
|
||||
// if the user wants to cancel the pending upload to execute that one
|
||||
common.uploadCancel(estimate, function (e) {
|
||||
if (e) {
|
||||
return void console.error(e);
|
||||
}
|
||||
next(again);
|
||||
});
|
||||
});
|
||||
}
|
||||
next(again);
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
return module;
|
||||
});
|
||||
|
||||
@@ -589,14 +589,9 @@ define([
|
||||
// Fix channel
|
||||
if (!el.channel) {
|
||||
try {
|
||||
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);
|
||||
console.log('Adding missing channel in filesData ', el.channel);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ define([
|
||||
};
|
||||
|
||||
exp.removeOwnedChannel = function (channel, cb) {
|
||||
if (typeof(channel) !== 'string' || channel.length !== 32) {
|
||||
if (typeof(channel) !== 'string' || [32,48].indexOf(channel.length) === -1) {
|
||||
// can't use this on files because files can't be owned...
|
||||
return void cb('INVALID_ARGUMENTS');
|
||||
}
|
||||
@@ -176,8 +176,19 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
exp.uploadComplete = function (cb) {
|
||||
rpc.send('UPLOAD_COMPLETE', null, function (e, res) {
|
||||
exp.uploadComplete = function (id, cb) {
|
||||
rpc.send('UPLOAD_COMPLETE', id, function (e, res) {
|
||||
if (e) { return void cb(e); }
|
||||
var id = res[0];
|
||||
if (typeof(id) !== 'string') {
|
||||
return void cb('INVALID_ID');
|
||||
}
|
||||
cb(void 0, id);
|
||||
});
|
||||
};
|
||||
|
||||
exp.ownedUploadComplete = function (id, cb) {
|
||||
rpc.send('OWNED_UPLOAD_COMPLETE', id, function (e, res) {
|
||||
if (e) { return void cb(e); }
|
||||
var id = res[0];
|
||||
if (typeof(id) !== 'string') {
|
||||
@@ -203,8 +214,8 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
exp.uploadCancel = function (cb) {
|
||||
rpc.send('UPLOAD_CANCEL', void 0, function (e) {
|
||||
exp.uploadCancel = function (size, cb) {
|
||||
rpc.send('UPLOAD_CANCEL', size, function (e) {
|
||||
if (e) { return void cb(e); }
|
||||
cb();
|
||||
});
|
||||
|
||||
@@ -329,15 +329,11 @@ define([
|
||||
dropArea: $('.CodeMirror'),
|
||||
body: $('body'),
|
||||
onUploaded: function (ev, data) {
|
||||
//var cursor = editor.getCursor();
|
||||
//var cleanName = data.name.replace(/[\[\]]/g, '');
|
||||
//var text = '';
|
||||
// PASSWORD_FILES
|
||||
var parsed = Hash.parsePadUrl(data.url);
|
||||
var hexFileName = Util.base64ToHex(parsed.hashData.channel);
|
||||
var src = '/blob/' + hexFileName.slice(0,2) + '/' + hexFileName;
|
||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' +
|
||||
parsed.hashData.key + '"></media-tag>';
|
||||
var secret = Hash.getSecrets('file', parsed.hash, data.password);
|
||||
var src = Hash.getBlobPathFromHex(secret.channel);
|
||||
var key = Hash.encodeBase64(secret.keys.cryptKey);
|
||||
var mt = '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '"></media-tag>';
|
||||
editor.replaceSelection(mt);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,11 +3,13 @@ define([
|
||||
'/file/file-crypto.js',
|
||||
'/common/common-thumbnail.js',
|
||||
'/common/common-interface.js',
|
||||
'/common/common-ui-elements.js',
|
||||
'/common/common-util.js',
|
||||
'/common/hyperscript.js',
|
||||
'/customize/messages.js',
|
||||
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function ($, FileCrypto, Thumb, UI, Util, Messages) {
|
||||
], function ($, FileCrypto, Thumb, UI, UIElements, Util, h, Messages) {
|
||||
var Nacl = window.nacl;
|
||||
var module = {};
|
||||
|
||||
@@ -26,6 +28,7 @@ define([
|
||||
|
||||
module.create = function (common, config) {
|
||||
var File = {};
|
||||
var origin = common.getMetadataMgr().getPrivateData().origin;
|
||||
|
||||
var queue = File.queue = {
|
||||
queue: [],
|
||||
@@ -53,6 +56,7 @@ define([
|
||||
|
||||
data.name = file.metadata.name;
|
||||
data.url = href;
|
||||
data.password = file.password;
|
||||
if (file.metadata.type.slice(0,6) === 'image/') {
|
||||
data.mediatag = true;
|
||||
}
|
||||
@@ -212,29 +216,61 @@ define([
|
||||
queue.next();
|
||||
};
|
||||
|
||||
// Don't show the rename prompt if we don't want to store the file in the drive (avatar)
|
||||
var showNamePrompt = !config.noStore;
|
||||
|
||||
var promptName = function (file, cb) {
|
||||
// Get the upload options
|
||||
var fileUploadModal = function (file, cb) {
|
||||
var extIdx = file.name.lastIndexOf('.');
|
||||
var name = extIdx !== -1 ? file.name.slice(0,extIdx) : file.name;
|
||||
var ext = extIdx !== -1 ? file.name.slice(extIdx) : "";
|
||||
var msg = Messages._getKey('upload_rename', [
|
||||
Util.fixHTML(file.name),
|
||||
Util.fixHTML(ext)
|
||||
|
||||
var createHelper = function (href, text) {
|
||||
var q = h('a.fa.fa-question-circle', {
|
||||
style: 'text-decoration: none !important;',
|
||||
title: text,
|
||||
href: origin + href,
|
||||
target: "_blank",
|
||||
'data-tippy-placement': "right"
|
||||
});
|
||||
return q;
|
||||
};
|
||||
|
||||
// Ask for name, password and owner
|
||||
var content = h('div', [
|
||||
h('h4', Messages.upload_modal_title),
|
||||
UIElements.setHTML(h('label', {for: 'cp-upload-name'}),
|
||||
Messages._getKey('upload_modal_filename', [ext])),
|
||||
h('input#cp-upload-name', {type: 'text', placeholder: name}),
|
||||
h('label', {for: 'cp-upload-password'}, Messages.creation_passwordValue),
|
||||
UI.passwordInput({id: 'cp-upload-password'}),
|
||||
h('span', {
|
||||
style: 'display:flex;align-items:center;justify-content:space-between'
|
||||
}, [
|
||||
UI.createCheckbox('cp-upload-owned', Messages.upload_modal_owner, true),
|
||||
createHelper('/faq.html#keywords-owned', Messages.creation_owned1)
|
||||
]),
|
||||
]);
|
||||
UI.prompt(msg, name, function (newName) {
|
||||
if (newName === null) {
|
||||
showNamePrompt = false;
|
||||
return void cb (file.name);
|
||||
}
|
||||
if (!newName || !newName.trim()) { return void cb (file.name); }
|
||||
|
||||
UI.confirm(content, function (yes) {
|
||||
if (!yes) { return void cb(); }
|
||||
|
||||
// Get the values
|
||||
var newName = $(content).find('#cp-upload-name').val();
|
||||
var password = $(content).find('#cp-upload-password').val() || undefined;
|
||||
var owned = $(content).find('#cp-upload-owned').is(':checked');
|
||||
|
||||
// Add extension to the name if needed
|
||||
if (!newName || !newName.trim()) { newName = file.name; }
|
||||
var newExtIdx = newName.lastIndexOf('.');
|
||||
var newExt = newExtIdx !== -1 ? newName.slice(newExtIdx) : "";
|
||||
if (newExt !== ext) { newName += ext; }
|
||||
cb(newName);
|
||||
}, {cancel: Messages.doNotAskAgain}, true);
|
||||
|
||||
cb({
|
||||
name: newName,
|
||||
password: password,
|
||||
owned: owned
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var handleFileState = {
|
||||
queue: [],
|
||||
inProgress: false
|
||||
@@ -246,17 +282,23 @@ define([
|
||||
var thumb;
|
||||
var file_arraybuffer;
|
||||
var name = file.name;
|
||||
var finish = function () {
|
||||
var metadata = {
|
||||
name: name,
|
||||
type: file.type,
|
||||
};
|
||||
if (thumb) { metadata.thumbnail = thumb; }
|
||||
queue.push({
|
||||
blob: file_arraybuffer,
|
||||
metadata: metadata,
|
||||
dropEvent: e
|
||||
});
|
||||
var password;
|
||||
var owned = true;
|
||||
var finish = function (abort) {
|
||||
if (!abort) {
|
||||
var metadata = {
|
||||
name: name,
|
||||
type: file.type,
|
||||
};
|
||||
if (thumb) { metadata.thumbnail = thumb; }
|
||||
queue.push({
|
||||
blob: file_arraybuffer,
|
||||
metadata: metadata,
|
||||
password: password,
|
||||
owned: owned,
|
||||
dropEvent: e
|
||||
});
|
||||
}
|
||||
handleFileState.inProgress = false;
|
||||
if (handleFileState.queue.length) {
|
||||
var next = handleFileState.queue.shift();
|
||||
@@ -264,9 +306,16 @@ define([
|
||||
}
|
||||
};
|
||||
var getName = function () {
|
||||
if (!showNamePrompt) { return void finish(); }
|
||||
promptName(file, function (newName) {
|
||||
name = newName;
|
||||
// If "noStore", it means we don't want to store this file in our drive (avatar)
|
||||
// In this case, we don't want a password or a filename, and we own the file
|
||||
if (config.noStore) { return void finish(); }
|
||||
|
||||
// Otherwise, ask for password, name and ownership
|
||||
fileUploadModal(file, function (obj) {
|
||||
if (!obj) { return void finish(true); }
|
||||
name = obj.name;
|
||||
password = obj.password;
|
||||
owned = obj.owned;
|
||||
finish();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -150,12 +150,12 @@ define([
|
||||
todo();
|
||||
} else {
|
||||
// Ask for the password and check if the pad exists
|
||||
// If the pad doesn't exist, it means the password is oncorrect
|
||||
// If the pad doesn't exist, it means the password isn't correct
|
||||
// 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) {
|
||||
var next = function (e, isNew) {
|
||||
if (Boolean(isNew)) {
|
||||
// Ask again in the inner iframe
|
||||
// We should receive a new Q_PAD_PASSWORD_VALUE
|
||||
@@ -165,7 +165,17 @@ define([
|
||||
correctPassword();
|
||||
cb(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
if (parsed.type === "file") {
|
||||
// `isNewChannel` doesn't work for files (not a channel)
|
||||
// `getFileSize` is not adapted to channels because of metadata
|
||||
Cryptpad.getFileSize(window.location.href, password, function (e, size) {
|
||||
next(e, size === 0);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Not a file, so we can use `isNewChannel`
|
||||
Cryptpad.isNewChannel(window.location.href, password, next);
|
||||
});
|
||||
sframeChan.event("EV_PAD_PASSWORD");
|
||||
}
|
||||
|
||||
@@ -113,16 +113,16 @@ define([
|
||||
return '<script src="' + origin + '/common/media-tag-nacl.min.js"></script>';
|
||||
};
|
||||
funcs.getMediatagFromHref = function (href) {
|
||||
// PASSWORD_FILES
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var secret = Hash.getSecrets('file', parsed.hash);
|
||||
// XXX: Should only be used with the current href
|
||||
var data = ctx.metadataMgr.getPrivateData();
|
||||
var parsed = Hash.parsePadUrl(href);
|
||||
var secret = Hash.getSecrets('file', parsed.hash, data.password);
|
||||
if (secret.keys && secret.channel) {
|
||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
||||
var hexFileName = Util.base64ToHex(secret.channel);
|
||||
var key = Hash.encodeBase64(secret.keys && secret.keys.cryptKey);
|
||||
var hexFileName = secret.channel;
|
||||
var origin = data.fileHost || data.origin;
|
||||
var src = origin + Hash.getBlobPathFromHex(hexFileName);
|
||||
return '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + cryptKey + '">' +
|
||||
return '<media-tag src="' + src + '" data-crypto-key="cryptpad:' + key + '">' +
|
||||
'</media-tag>';
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -590,7 +590,6 @@ define([
|
||||
}
|
||||
}, cb);
|
||||
}
|
||||
console.log(path, newName);
|
||||
if (path.length <= 1) {
|
||||
logError('Renaming `root` is forbidden');
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user