Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging
This commit is contained in:
commit
89a993be3c
@ -116,6 +116,12 @@ module.exports = {
|
|||||||
'contact',
|
'contact',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/* Domain
|
||||||
|
* If you want to have enable payments on your CryptPad instance, it has to be able to tell
|
||||||
|
* our account server what is your domain
|
||||||
|
*/
|
||||||
|
// domain: 'https://cryptpad.fr',
|
||||||
|
|
||||||
/*
|
/*
|
||||||
You have the option of specifying an alternative storage adaptor.
|
You have the option of specifying an alternative storage adaptor.
|
||||||
These status of these alternatives are specified in their READMEs,
|
These status of these alternatives are specified in their READMEs,
|
||||||
|
|||||||
@ -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": "1.6.0",
|
"version": "1.8.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chainpad-server": "^1.0.1",
|
"chainpad-server": "^1.0.1",
|
||||||
"express": "~4.10.1",
|
"express": "~4.10.1",
|
||||||
|
|||||||
84
rpc.js
84
rpc.js
@ -7,11 +7,14 @@ var Nacl = require("tweetnacl");
|
|||||||
|
|
||||||
var Fs = require("fs");
|
var Fs = require("fs");
|
||||||
var Path = require("path");
|
var Path = require("path");
|
||||||
|
var Https = require("https");
|
||||||
|
|
||||||
var RPC = module.exports;
|
var RPC = module.exports;
|
||||||
|
|
||||||
var Store = require("./storage/file");
|
var Store = require("./storage/file");
|
||||||
|
|
||||||
|
var DEFAULT_LIMIT = 100;
|
||||||
|
|
||||||
var isValidChannel = function (chan) {
|
var isValidChannel = function (chan) {
|
||||||
return /^[a-fA-F0-9]/.test(chan) ||
|
return /^[a-fA-F0-9]/.test(chan) ||
|
||||||
[32, 48].indexOf(chan.length) !== -1;
|
[32, 48].indexOf(chan.length) !== -1;
|
||||||
@ -454,8 +457,64 @@ var isPrivilegedUser = function (publicKey, cb) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var getLimit = function (cb) {
|
// The limits object contains storage limits for all the publicKey that have paid
|
||||||
cb = cb; // TODO
|
// To each key is associated an object containing the 'limit' value and a 'note' explaining that limit
|
||||||
|
var limits = {};
|
||||||
|
var updateLimits = function (config, publicKey, cb) {
|
||||||
|
if (typeof cb !== "function") { cb = function () {}; }
|
||||||
|
|
||||||
|
var body = JSON.stringify({
|
||||||
|
domain: config.domain,
|
||||||
|
subdomain: config.subdomain
|
||||||
|
});
|
||||||
|
var options = {
|
||||||
|
host: 'accounts.cryptpad.fr',
|
||||||
|
path: '/api/getauthorized',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Content-Length": Buffer.byteLength(body)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var req = Https.request(options, function (response) {
|
||||||
|
if (!('' + req.statusCode).match(/^2\d\d$/)) {
|
||||||
|
return void cb('SERVER ERROR ' + req.statusCode);
|
||||||
|
}
|
||||||
|
var str = '';
|
||||||
|
|
||||||
|
response.on('data', function (chunk) {
|
||||||
|
str += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
response.on('end', function () {
|
||||||
|
try {
|
||||||
|
var json = JSON.parse(str);
|
||||||
|
limits = json;
|
||||||
|
var l;
|
||||||
|
if (publicKey) {
|
||||||
|
var limit = limits[publicKey];
|
||||||
|
l = limit && typeof limit.limit === "number" ? limit.limit : DEFAULT_LIMIT;
|
||||||
|
}
|
||||||
|
cb(void 0, l);
|
||||||
|
} catch (e) {
|
||||||
|
cb(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
req.on('error', function (e) {
|
||||||
|
if (!config.domain) { return cb(); }
|
||||||
|
cb(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
req.end(body);
|
||||||
|
};
|
||||||
|
|
||||||
|
var getLimit = function (publicKey, cb) {
|
||||||
|
var limit = limits[publicKey];
|
||||||
|
|
||||||
|
cb(void 0, limit && typeof(limit.limit) === "number"?
|
||||||
|
limit.limit : DEFAULT_LIMIT);
|
||||||
};
|
};
|
||||||
|
|
||||||
var safeMkdir = function (path, cb) {
|
var safeMkdir = function (path, cb) {
|
||||||
@ -714,10 +773,16 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
|
|||||||
});
|
});
|
||||||
case 'GET_FILE_SIZE':
|
case 'GET_FILE_SIZE':
|
||||||
return void getFileSize(ctx.store, msg[1], Respond);
|
return void getFileSize(ctx.store, msg[1], Respond);
|
||||||
case 'GET_LIMIT': // TODO implement this and cache it per-user
|
case 'UPDATE_LIMITS':
|
||||||
return void getLimit(function (e, limit) {
|
return void updateLimits(config, safeKey, function (e, limit) {
|
||||||
|
if (e) { return void Respond(e); }
|
||||||
|
Respond(void 0, limit);
|
||||||
|
});
|
||||||
|
case 'GET_LIMIT':
|
||||||
|
return void getLimit(safeKey, function (e, limit) {
|
||||||
|
if (e) { return void Respond(e); }
|
||||||
limit = limit;
|
limit = limit;
|
||||||
Respond('NOT_IMPLEMENTED');
|
Respond(void 0, limit);
|
||||||
});
|
});
|
||||||
case 'GET_MULTIPLE_FILE_SIZE':
|
case 'GET_MULTIPLE_FILE_SIZE':
|
||||||
return void getMultipleFileSize(ctx.store, msg[1], function (e, dict) {
|
return void getMultipleFileSize(ctx.store, msg[1], function (e, dict) {
|
||||||
@ -725,7 +790,6 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
|
|||||||
Respond(void 0, dict);
|
Respond(void 0, dict);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// restricted to privileged users...
|
// restricted to privileged users...
|
||||||
case 'UPLOAD':
|
case 'UPLOAD':
|
||||||
if (!privileged) { return deny(); }
|
if (!privileged) { return deny(); }
|
||||||
@ -775,6 +839,14 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
|
|||||||
handleMessage(session.privilege);
|
handleMessage(session.privilege);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var updateLimitDaily = function () {
|
||||||
|
updateLimits(config, undefined, function (e) {
|
||||||
|
if (e) { console.error('Error updating the storage limits', e); }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
updateLimitDaily();
|
||||||
|
setInterval(updateLimitDaily, 24*3600*1000);
|
||||||
|
|
||||||
Store.create({
|
Store.create({
|
||||||
filePath: pinPath,
|
filePath: pinPath,
|
||||||
}, function (s) {
|
}, function (s) {
|
||||||
|
|||||||
@ -21,7 +21,7 @@ define([], function () {
|
|||||||
.replace(/ +$/, "")
|
.replace(/ +$/, "")
|
||||||
.split(" ");
|
.split(" ");
|
||||||
var byteString = String.fromCharCode.apply(null, hexArray);
|
var byteString = String.fromCharCode.apply(null, hexArray);
|
||||||
return window.btoa(byteString).replace(/\//g, '-').slice(0,-2);
|
return window.btoa(byteString).replace(/\//g, '-').replace(/=+$/, '');
|
||||||
};
|
};
|
||||||
|
|
||||||
Util.base64ToHex = function (b64String) {
|
Util.base64ToHex = function (b64String) {
|
||||||
@ -90,11 +90,21 @@ define([], function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Util.fetch = function (src, cb) {
|
Util.fetch = function (src, cb) {
|
||||||
|
var done = false;
|
||||||
|
var CB = function (err, res) {
|
||||||
|
if (done) { return; }
|
||||||
|
done = true;
|
||||||
|
cb(err, res);
|
||||||
|
};
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open("GET", src, true);
|
xhr.open("GET", src, true);
|
||||||
xhr.responseType = "arraybuffer";
|
xhr.responseType = "arraybuffer";
|
||||||
xhr.onload = function () {
|
xhr.onload = function () {
|
||||||
return void cb(void 0, new Uint8Array(xhr.response));
|
return void CB(void 0, new Uint8Array(xhr.response));
|
||||||
|
};
|
||||||
|
xhr.onerror = function () {
|
||||||
|
CB('XHR_ERROR');
|
||||||
};
|
};
|
||||||
xhr.send(null);
|
xhr.send(null);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -746,8 +746,15 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
common.updatePinLimit = function (cb) {
|
||||||
|
if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); }
|
||||||
|
rpc.getFileListSize(cb);
|
||||||
|
};
|
||||||
|
|
||||||
common.getPinLimit = function (cb) {
|
common.getPinLimit = function (cb) {
|
||||||
cb(void 0, typeof(AppConfig.pinLimit) === 'number'? AppConfig.pinLimit: 1000);
|
if (!pinsReady()) { return void cb('[RPC_NOT_READY]'); }
|
||||||
|
rpc.getFileListSize(cb);
|
||||||
|
//cb(void 0, typeof(AppConfig.pinLimit) === 'number'? AppConfig.pinLimit: 1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
common.isOverPinLimit = function (cb) {
|
common.isOverPinLimit = function (cb) {
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -121,6 +121,29 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Update the limit value for all the users and return the limit for your publicKey
|
||||||
|
exp.updatePinLimits = function (cb) {
|
||||||
|
rpc.send('UPDATE_LIMITS', undefined, function (e, response) {
|
||||||
|
if (e) { return void cb(e); }
|
||||||
|
if (response && typeof response === "number") {
|
||||||
|
cb (void 0, response);
|
||||||
|
} else {
|
||||||
|
cb('INVALID_RESPONSE');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// Get the storage limit associated with your publicKey
|
||||||
|
exp.getLimit = function (cb) {
|
||||||
|
rpc.send('GET_LIMIT', undefined, function (e, response) {
|
||||||
|
if (e) { return void cb(e); }
|
||||||
|
if (response && typeof response === "number") {
|
||||||
|
cb (void 0, response);
|
||||||
|
} else {
|
||||||
|
cb('INVALID_RESPONSE');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
cb(e, exp);
|
cb(e, exp);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -7,6 +7,12 @@ define([
|
|||||||
var plainChunkLength = 128 * 1024;
|
var plainChunkLength = 128 * 1024;
|
||||||
var cypherChunkLength = 131088;
|
var cypherChunkLength = 131088;
|
||||||
|
|
||||||
|
var computeEncryptedSize = function (bytes, meta) {
|
||||||
|
var metasize = Nacl.util.decodeUTF8(JSON.stringify(meta)).length;
|
||||||
|
var chunks = Math.ceil(bytes / plainChunkLength);
|
||||||
|
return metasize + 18 + (chunks * 16) + bytes;
|
||||||
|
};
|
||||||
|
|
||||||
var encodePrefix = function (p) {
|
var encodePrefix = function (p) {
|
||||||
return [
|
return [
|
||||||
65280, // 255 << 8
|
65280, // 255 << 8
|
||||||
@ -131,23 +137,15 @@ define([
|
|||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
/*
|
|
||||||
0: metadata
|
|
||||||
1: u8
|
|
||||||
2: done
|
|
||||||
*/
|
|
||||||
|
|
||||||
var state = 0;
|
var state = 0;
|
||||||
|
|
||||||
var next = function (cb) {
|
var next = function (cb) {
|
||||||
|
if (state === 2) { return void cb(); }
|
||||||
|
|
||||||
var start;
|
var start;
|
||||||
var end;
|
var end;
|
||||||
var part;
|
var part;
|
||||||
var box;
|
var box;
|
||||||
|
|
||||||
// DONE
|
|
||||||
if (state === 2) { return void cb(); }
|
|
||||||
|
|
||||||
if (state === 0) { // metadata...
|
if (state === 0) { // metadata...
|
||||||
part = new Uint8Array(plaintext);
|
part = new Uint8Array(plaintext);
|
||||||
box = Nacl.secretbox(part, nonce, key);
|
box = Nacl.secretbox(part, nonce, key);
|
||||||
@ -159,6 +157,7 @@ define([
|
|||||||
var prefixed = new Uint8Array(encodePrefix(box.length)
|
var prefixed = new Uint8Array(encodePrefix(box.length)
|
||||||
.concat(slice(box)));
|
.concat(slice(box)));
|
||||||
state++;
|
state++;
|
||||||
|
|
||||||
return void cb(void 0, prefixed);
|
return void cb(void 0, prefixed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,5 +183,6 @@ define([
|
|||||||
decrypt: decrypt,
|
decrypt: decrypt,
|
||||||
encrypt: encrypt,
|
encrypt: encrypt,
|
||||||
joinChunks: joinChunks,
|
joinChunks: joinChunks,
|
||||||
|
computeEncryptedSize: computeEncryptedSize,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@ -36,6 +36,7 @@ define([
|
|||||||
var key = Nacl.randomBytes(32);
|
var key = Nacl.randomBytes(32);
|
||||||
var next = FileCrypto.encrypt(u8, metadata, key);
|
var next = FileCrypto.encrypt(u8, metadata, key);
|
||||||
|
|
||||||
|
var estimate = FileCrypto.computeEncryptedSize(blob.byteLength, metadata);
|
||||||
var chunks = [];
|
var chunks = [];
|
||||||
|
|
||||||
var sendChunk = function (box, cb) {
|
var sendChunk = function (box, cb) {
|
||||||
@ -47,15 +48,21 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var actual = 0;
|
||||||
var again = function (err, box) {
|
var again = function (err, box) {
|
||||||
if (err) { throw new Error(err); }
|
if (err) { throw new Error(err); }
|
||||||
if (box) {
|
if (box) {
|
||||||
|
actual += box.length;
|
||||||
return void sendChunk(box, function (e) {
|
return void sendChunk(box, function (e) {
|
||||||
if (e) { return console.error(e); }
|
if (e) { return console.error(e); }
|
||||||
next(again);
|
next(again);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (actual !== estimate) {
|
||||||
|
console.error('Estimated size does not match actual size');
|
||||||
|
}
|
||||||
|
|
||||||
// if not box then done
|
// if not box then done
|
||||||
Cryptpad.rpc.send('UPLOAD_COMPLETE', '', function (e, res) {
|
Cryptpad.rpc.send('UPLOAD_COMPLETE', '', function (e, res) {
|
||||||
if (e) { return void console.error(e); }
|
if (e) { return void console.error(e); }
|
||||||
@ -168,11 +175,15 @@ define([
|
|||||||
if (!uploadMode) {
|
if (!uploadMode) {
|
||||||
var src = Cryptpad.getBlobPathFromHex(hexFileName);
|
var src = Cryptpad.getBlobPathFromHex(hexFileName);
|
||||||
return Cryptpad.fetch(src, function (e, u8) {
|
return Cryptpad.fetch(src, function (e, u8) {
|
||||||
|
if (e) { return void Cryptpad.alert(e); }
|
||||||
// now decrypt the u8
|
// now decrypt the u8
|
||||||
if (e) { return window.alert('error'); }
|
|
||||||
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
var cryptKey = secret.keys && secret.keys.fileKeyStr;
|
||||||
var key = Nacl.util.decodeBase64(cryptKey);
|
var key = Nacl.util.decodeBase64(cryptKey);
|
||||||
|
|
||||||
|
if (!u8 || !u8.length) {
|
||||||
|
return void Cryptpad.errorLoadingScreen(e);
|
||||||
|
}
|
||||||
|
|
||||||
FileCrypto.decrypt(u8, key, function (e, data) {
|
FileCrypto.decrypt(u8, key, function (e, data) {
|
||||||
if (e) {
|
if (e) {
|
||||||
Cryptpad.removeLoadingScreen();
|
Cryptpad.removeLoadingScreen();
|
||||||
|
|||||||
@ -16,6 +16,8 @@
|
|||||||
}
|
}
|
||||||
media-tag * {
|
media-tag * {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@ -20,6 +20,11 @@ define([
|
|||||||
Cryptpad.addLoadingScreen();
|
Cryptpad.addLoadingScreen();
|
||||||
|
|
||||||
var andThen = function () {
|
var andThen = function () {
|
||||||
|
$(window.document).on('decryption', function (e) {
|
||||||
|
var decrypted = e.originalEvent;
|
||||||
|
console.log(decrypted.blob, decrypted.metadata);
|
||||||
|
});
|
||||||
|
|
||||||
var $bar = $iframe.find('.toolbar-container');
|
var $bar = $iframe.find('.toolbar-container');
|
||||||
var secret = Cryptpad.getSecrets();
|
var secret = Cryptpad.getSecrets();
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user