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

pull/1/head
yflory 8 years ago
commit b76dcba1d6

@ -13,7 +13,8 @@ var RPC = module.exports;
var Store = require("./storage/file"); var Store = require("./storage/file");
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;
}; };
var uint8ArrayToHex = function (a) { var uint8ArrayToHex = function (a) {
@ -33,10 +34,10 @@ var uint8ArrayToHex = function (a) {
}).join(''); }).join('');
}; };
var createChannelId = function () { var createFileId = function () {
var id = uint8ArrayToHex(Nacl.randomBytes(16)); var id = uint8ArrayToHex(Nacl.randomBytes(24));
if (id.length !== 32 || /[^a-f0-9]/.test(id)) { if (id.length !== 48 || /[^a-f0-9]/.test(id)) {
throw new Error('channel ids must consist of 32 hex characters'); throw new Error('file ids must consist of 48 hex characters');
} }
return id; return id;
}; };
@ -245,8 +246,19 @@ var getChannelList = function (store, Sessions, publicKey, cb) {
}); });
}; };
var getUploadSize = function (store, channel, cb) {
var path = '';
Fs.stat(path, function (err, stats) {
if (err) { return void cb(err); }
cb(void 0, stats.size);
});
};
var getFileSize = function (store, channel, cb) { var getFileSize = function (store, channel, cb) {
if (!isValidChannel(channel)) { return void cb('INVALID_CHAN'); } if (!isValidChannel(channel)) { return void cb('INVALID_CHAN'); }
if (channel.length === 32) {
if (typeof(store.getChannelSize) !== 'function') { if (typeof(store.getChannelSize) !== 'function') {
return cb('GET_CHANNEL_SIZE_UNSUPPORTED'); return cb('GET_CHANNEL_SIZE_UNSUPPORTED');
} }
@ -255,6 +267,13 @@ var getFileSize = function (store, channel, cb) {
if (e) { return void cb(e.code); } if (e) { return void cb(e.code); }
cb(void 0, size); cb(void 0, size);
}); });
}
// 'channel' refers to a file, so you need anoter API
getUploadSize(null, channel, function (e, size) {
if (e) { return void cb(e); }
cb(void 0, size);
});
}; };
var getMultipleFileSize = function (store, channels, cb) { var getMultipleFileSize = function (store, channels, cb) {
@ -332,6 +351,7 @@ var getHash = function (store, Sessions, publicKey, cb) {
store.message(publicKey, JSON.stringify(msg), cb); store.message(publicKey, JSON.stringify(msg), cb);
}; */ }; */
// TODO check if new pinned size exceeds user quota
var pinChannel = function (store, Sessions, publicKey, channels, cb) { var pinChannel = function (store, Sessions, publicKey, channels, cb) {
if (!channels && channels.filter) { if (!channels && channels.filter) {
// expected array // expected array
@ -383,7 +403,7 @@ var unpinChannel = function (store, Sessions, publicKey, channels, cb) {
function (e) { function (e) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
toStore.forEach(function (channel) { toStore.forEach(function (channel) {
delete session.channels[channel]; // = false; delete session.channels[channel];
}); });
getHash(store, Sessions, publicKey, cb); getHash(store, Sessions, publicKey, cb);
@ -391,6 +411,7 @@ var unpinChannel = function (store, Sessions, publicKey, channels, cb) {
}); });
}; };
// TODO check if new pinned size exceeds user quota
var resetUserPins = function (store, Sessions, publicKey, channelList, cb) { var resetUserPins = function (store, Sessions, publicKey, channelList, cb) {
var session = beginSession(Sessions, publicKey); var session = beginSession(Sessions, publicKey);
@ -469,13 +490,13 @@ var makeFileStream = function (root, id, cb) {
}); });
}; };
var upload = function (stagingPath, Sessions, publicKey, content, cb) { var upload = function (paths, Sessions, publicKey, content, cb) {
var dec = new Buffer(Nacl.util.decodeBase64(content)); // jshint ignore:line var dec = new Buffer(Nacl.util.decodeBase64(content)); // jshint ignore:line
var session = Sessions[publicKey]; var session = Sessions[publicKey];
session.atime = +new Date(); session.atime = +new Date();
if (!session.blobstage) { if (!session.blobstage) {
makeFileStream(stagingPath, publicKey, function (e, stream) { makeFileStream(paths.staging, publicKey, function (e, stream) {
if (e) { return void cb(e); } if (e) { return void cb(e); }
var blobstage = session.blobstage = stream; var blobstage = session.blobstage = stream;
@ -488,10 +509,10 @@ var upload = function (stagingPath, Sessions, publicKey, content, cb) {
} }
}; };
var upload_cancel = function (stagingPath, Sessions, publicKey, cb) { var upload_cancel = function (paths, Sessions, publicKey, cb) {
var path = makeFilePath(stagingPath, publicKey); var path = makeFilePath(paths.staging, publicKey);
if (!path) { if (!path) {
console.log(stagingPath, publicKey); console.log(paths.staging, publicKey);
console.log(path); console.log(path);
return void cb('NO_FILE'); return void cb('NO_FILE');
} }
@ -512,7 +533,13 @@ var isFile = function (filePath, cb) {
}); });
}; };
var upload_complete = function (stagingPath, storePath, Sessions, publicKey, cb) { /* TODO
change channel IDs to a different length so that when we pin, we will be able
to tell that it is not a channel, but a file, just by its length.
also, when your upload is complete, pin the resulting file.
*/
var upload_complete = function (paths, Sessions, publicKey, cb) {
var session = Sessions[publicKey]; var session = Sessions[publicKey];
if (session.blobstage && session.blobstage.close) { if (session.blobstage && session.blobstage.close) {
@ -520,14 +547,14 @@ var upload_complete = function (stagingPath, storePath, Sessions, publicKey, cb)
delete session.blobstage; delete session.blobstage;
} }
var oldPath = makeFilePath(stagingPath, publicKey); var oldPath = makeFilePath(paths.staging, publicKey);
var tryRandomLocation = function (cb) { var tryRandomLocation = function (cb) {
var id = createChannelId(); var id = createFileId();
var prefix = id.slice(0, 2); var prefix = id.slice(0, 2);
var newPath = makeFilePath(storePath, id); var newPath = makeFilePath(paths.blob, id);
safeMkdir(Path.join(storePath, prefix), function (e) { safeMkdir(Path.join(paths.blob, prefix), function (e) {
if (e) { if (e) {
console.error(e); console.error(e);
return void cb('RENAME_ERR'); return void cb('RENAME_ERR');
@ -558,8 +585,14 @@ var upload_complete = function (stagingPath, storePath, Sessions, publicKey, cb)
}); });
}; };
var upload_status = function (stagingPath, Sessions, publicKey, cb) { /* TODO
var filePath = makeFilePath(stagingPath, publicKey); when asking about your upload status, also send some information about how big
your upload is going to be. if that would exceed your limit, return TOO_LARGE
error.
*/
var upload_status = function (paths, Sessions, publicKey, cb) {
var filePath = makeFilePath(paths.staging, publicKey);
if (!filePath) { return void cb('E_INVALID_PATH'); } if (!filePath) { return void cb('E_INVALID_PATH'); }
isFile(filePath, function (e, yes) { isFile(filePath, function (e, yes) {
cb(e, yes); cb(e, yes);
@ -577,9 +610,10 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
return typeof(config[key]) === 'string'? config[key]: def; return typeof(config[key]) === 'string'? config[key]: def;
}; };
var pinPath = keyOrDefaultString('pinPath', './pins'); var paths = {};
var blobPath = keyOrDefaultString('blobPath', './blob'); var pinPath = paths.pin = keyOrDefaultString('pinPath', './pins');
var blobStagingPath = keyOrDefaultString('blobStagingPath', './blobstage'); var blobPath = paths.blob = keyOrDefaultString('blobPath', './blob');
var blobStagingPath = paths.staging = keyOrDefaultString('blobStagingPath', './blobstage');
var store; var store;
@ -695,22 +729,22 @@ RPC.create = function (config /*:typeof(ConfigType)*/, cb /*:(?Error, ?Function)
// restricted to privileged users... // restricted to privileged users...
case 'UPLOAD': case 'UPLOAD':
if (!privileged) { return deny(); } if (!privileged) { return deny(); }
return void upload(blobStagingPath, Sessions, safeKey, msg[1], function (e, len) { return void upload(paths, Sessions, safeKey, msg[1], function (e, len) {
Respond(e, len); Respond(e, len);
}); });
case 'UPLOAD_STATUS': case 'UPLOAD_STATUS':
if (!privileged) { return deny(); } if (!privileged) { return deny(); }
return void upload_status(blobStagingPath, Sessions, safeKey, function (e, stat) { return void upload_status(paths, Sessions, safeKey, function (e, stat) {
Respond(e, stat); Respond(e, stat);
}); });
case 'UPLOAD_COMPLETE': case 'UPLOAD_COMPLETE':
if (!privileged) { return deny(); } if (!privileged) { return deny(); }
return void upload_complete(blobStagingPath, blobPath, Sessions, safeKey, function (e, hash) { return void upload_complete(paths, Sessions, safeKey, function (e, hash) {
Respond(e, hash); Respond(e, hash);
}); });
case 'UPLOAD_CANCEL': case 'UPLOAD_CANCEL':
if (!privileged) { return deny(); } if (!privileged) { return deny(); }
return void upload_cancel(blobStagingPath, Sessions, safeKey, function (e) { return void upload_cancel(paths, Sessions, safeKey, function (e) {
Respond(e); Respond(e);
}); });
default: default:

@ -15,27 +15,41 @@ define([
var failMessages = []; var failMessages = [];
var ASSERTS = []; var ASSERTS = [];
var runASSERTS = function () { var runASSERTS = function (cb) {
var count = ASSERTS.length;
var successes = 0;
var done = function (err) {
count--;
if (err) { failMessages.push(err); }
else { successes++; }
if (count === 0) { cb(); }
};
ASSERTS.forEach(function (f, index) { ASSERTS.forEach(function (f, index) {
f(index); f(function (err) {
done(err, index);
}, index);
}); });
}; };
var assert = function (test, msg) { var assert = function (test, msg) {
ASSERTS.push(function (i) { ASSERTS.push(function (cb, i) {
var returned = test(); test(function (result) {
if (returned === true) { if (result === true) {
assertions++; assertions++;
cb();
} else { } else {
failed = true; failed = true;
failedOn = assertions; failedOn = assertions;
failMessages.push({ cb({
test: i, test: i,
message: msg, message: msg,
output: returned, output: result,
}); });
} }
}); });
});
}; };
var HJSON_list = [ var HJSON_list = [
@ -58,7 +72,7 @@ define([
}; };
var HJSON_equal = function (shjson) { var HJSON_equal = function (shjson) {
assert(function () { assert(function (cb) {
// parse your stringified Hyperjson // parse your stringified Hyperjson
var hjson; var hjson;
@ -82,10 +96,10 @@ define([
var diff = TextPatcher.format(shjson, op); var diff = TextPatcher.format(shjson, op);
if (success) { if (success) {
return true; return cb(true);
} else { } else {
return '<br><br>insert: ' + diff.insert + '<br><br>' + return cb('<br><br>insert: ' + diff.insert + '<br><br>' +
'remove: ' + diff.remove + '<br><br>'; 'remove: ' + diff.remove + '<br><br>');
} }
}, "expected hyperjson equality"); }, "expected hyperjson equality");
}; };
@ -94,7 +108,7 @@ define([
var roundTrip = function (sel) { var roundTrip = function (sel) {
var target = $(sel)[0]; var target = $(sel)[0];
assert(function () { assert(function (cb) {
var hjson = Hyperjson.fromDOM(target); var hjson = Hyperjson.fromDOM(target);
var cloned = Hyperjson.toDOM(hjson); var cloned = Hyperjson.toDOM(hjson);
var success = cloned.outerHTML === target.outerHTML; var success = cloned.outerHTML === target.outerHTML;
@ -111,7 +125,7 @@ define([
TextPatcher.log(target.outerHTML, op); TextPatcher.log(target.outerHTML, op);
} }
return success; return cb(success);
}, "Round trip serialization introduced artifacts."); }, "Round trip serialization introduced artifacts.");
}; };
@ -125,9 +139,9 @@ define([
var strungJSON = function (orig) { var strungJSON = function (orig) {
var result; var result;
assert(function () { assert(function (cb) {
result = JSON.stringify(JSON.parse(orig)); result = JSON.stringify(JSON.parse(orig));
return result === orig; return cb(result === orig);
}, "expected result (" + result + ") to equal original (" + orig + ")"); }, "expected result (" + result + ") to equal original (" + orig + ")");
}; };
@ -138,46 +152,56 @@ define([
}); });
// check that old hashes parse correctly // check that old hashes parse correctly
assert(function () { assert(function (cb) {
var secret = Cryptpad.parseHash('67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy'); var secret = Cryptpad.parseHash('67b8385b07352be53e40746d2be6ccd7XAYSuJYYqa9NfmInyHci7LNy');
return secret.channel === "67b8385b07352be53e40746d2be6ccd7" && return cb(secret.channel === "67b8385b07352be53e40746d2be6ccd7" &&
secret.key === "XAYSuJYYqa9NfmInyHci7LNy" && secret.key === "XAYSuJYYqa9NfmInyHci7LNy" &&
secret.version === 0; secret.version === 0);
}, "Old hash failed to parse"); }, "Old hash failed to parse");
// make sure version 1 hashes parse correctly // make sure version 1 hashes parse correctly
assert(function () { assert(function (cb) {
var secret = Cryptpad.parseHash('/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI'); var secret = Cryptpad.parseHash('/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI');
return secret.version === 1 && return cb(secret.version === 1 &&
secret.mode === "edit" && secret.mode === "edit" &&
secret.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" && secret.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" &&
secret.key === "usn4+9CqVja8Q7RZOGTfRgqI" && secret.key === "usn4+9CqVja8Q7RZOGTfRgqI" &&
!secret.present; !secret.present);
}, "version 1 hash failed to parse"); }, "version 1 hash failed to parse");
// test support for present mode in hashes // test support for present mode in hashes
assert(function () { assert(function (cb) {
var secret = Cryptpad.parseHash('/1/edit/CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G/present'); var secret = Cryptpad.parseHash('/1/edit/CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G/present');
return secret.version === 1 return cb(secret.version === 1
&& secret.mode === "edit" && secret.mode === "edit"
&& secret.channel === "CmN5+YJkrHFS3NSBg-P7Sg" && secret.channel === "CmN5+YJkrHFS3NSBg-P7Sg"
&& secret.key === "DNZ2wcG683GscU4fyOyqA87G" && secret.key === "DNZ2wcG683GscU4fyOyqA87G"
&& secret.present; && secret.present);
}, "version 1 hash failed to parse"); }, "version 1 hash failed to parse");
// test support for present mode in hashes
assert(function (cb) {
var secret = Cryptpad.parseHash('/1/edit//CmN5+YJkrHFS3NSBg-P7Sg/DNZ2wcG683GscU4fyOyqA87G//present');
return cb(secret.version === 1
&& secret.mode === "edit"
&& secret.channel === "CmN5+YJkrHFS3NSBg-P7Sg"
&& secret.key === "DNZ2wcG683GscU4fyOyqA87G"
&& secret.present);
}, "Couldn't handle multiple successive slashes");
// test support for trailing slash // test support for trailing slash
assert(function () { assert(function (cb) {
var secret = Cryptpad.parseHash('/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI/'); var secret = Cryptpad.parseHash('/1/edit/3Ujt4F2Sjnjbis6CoYWpoQ/usn4+9CqVja8Q7RZOGTfRgqI/');
return secret.version === 1 && return cb(secret.version === 1 &&
secret.mode === "edit" && secret.mode === "edit" &&
secret.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" && secret.channel === "3Ujt4F2Sjnjbis6CoYWpoQ" &&
secret.key === "usn4+9CqVja8Q7RZOGTfRgqI" && secret.key === "usn4+9CqVja8Q7RZOGTfRgqI" &&
!secret.present; !secret.present);
}, "test support for trailing slashes in version 1 hash failed to parse"); }, "test support for trailing slashes in version 1 hash failed to parse");
assert(function () { assert(function (cb) {
// TODO // TODO
return true; return cb(true);
}, "version 2 hash failed to parse correctly"); }, "version 2 hash failed to parse correctly");
var swap = function (str, dict) { var swap = function (str, dict) {
@ -215,8 +239,7 @@ The test returned:
}).join("\n"); }).join("\n");
}; };
runASSERTS(); runASSERTS(function () {
$("body").html(function (i, val) { $("body").html(function (i, val) {
var dict = { var dict = {
previous: val, previous: val,
@ -244,5 +267,6 @@ The test returned:
var $report = $('.report'); var $report = $('.report');
$report.addClass(failed?'failure':'success'); $report.addClass(failed?'failure':'success');
});
}); });

@ -66,6 +66,10 @@ define([
return '/' + parsed.type + '/#' + parsed.hash; return '/' + parsed.type + '/#' + parsed.hash;
}; };
var fixDuplicateSlashes = function (s) {
return s.replace(/\/+/g, '/');
};
/* /*
* Returns all needed keys for a realtime channel * Returns all needed keys for a realtime channel
* - 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
@ -95,7 +99,7 @@ define([
} }
else { else {
// New hash // New hash
var hashArray = hash.split('/'); var hashArray = fixDuplicateSlashes(hash).split('/');
if (hashArray.length < 4) { if (hashArray.length < 4) {
Hash.alert("Unable to parse the key"); Hash.alert("Unable to parse the key");
throw new Error("Unable to parse the key"); throw new Error("Unable to parse the key");
@ -179,7 +183,7 @@ Version 2
parsed.version = 0; parsed.version = 0;
return parsed; return parsed;
} }
var hashArr = hash.split('/'); var hashArr = fixDuplicateSlashes(hash).split('/');
if (hashArr[1] && hashArr[1] === '1') { if (hashArr[1] && hashArr[1] === '1') {
parsed.version = 1; parsed.version = 1;
parsed.mode = hashArr[2]; parsed.mode = hashArr[2];

@ -57,55 +57,30 @@ define([
}, [])); }, []));
}; };
var padChunk = function (A) { var decrypt = function (u8, key, cb) {
var padding; var fail = function (e) {
if (A.length === plainChunkLength) { return A; } cb(e || "DECRYPTION_ERROR");
if (A.length < plainChunkLength) {
padding = new Array(plainChunkLength - A.length).fill(32);
return A.concat(padding);
}
if (A.length > plainChunkLength) {
// how many times larger is it?
var chunks = Math.ceil(A.length / plainChunkLength);
padding = new Array((plainChunkLength * chunks) - A.length).fill(32);
return A.concat(padding);
}
}; };
var decrypt = function (u8, key, cb) {
var nonce = createNonce(); var nonce = createNonce();
var i = 0; var i = 0;
decodePrefix([]); // TODO var prefix = u8.subarray(0, 2);
var takeChunk = function () { var metadataLength = decodePrefix(prefix);
var start = i * cypherChunkLength;
var end = start + cypherChunkLength;
i++;
var box = new Uint8Array(u8.subarray(start, end));
// decrypt the chunk
var plaintext = Nacl.secretbox.open(box, nonce, key);
increment(nonce);
return plaintext;
};
var buffer = '';
var res = { var res = {
metadata: undefined, metadata: undefined,
}; };
// decrypt metadata var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength));
var chunk;
for (; !res.metadata && i * cypherChunkLength < u8.length;) { var metaChunk = Nacl.secretbox.open(metaBox, nonce, key);
chunk = takeChunk(); increment(nonce);
buffer += Nacl.util.encodeUTF8(chunk);
try { try {
res.metadata = JSON.parse(buffer); res.metadata = JSON.parse(Nacl.util.encodeUTF8(metaChunk));
//console.log(res.metadata);
} catch (e) { } catch (e) {
console.log('buffering another chunk for metadata'); return fail('E_METADATA_DECRYPTION');
}
} }
if (!res.metadata) { if (!res.metadata) {
@ -114,12 +89,21 @@ define([
}); });
} }
var fail = function () { var takeChunk = function () {
cb("DECRYPTION_ERROR"); var start = i * cypherChunkLength + 2 + metadataLength;
var end = start + cypherChunkLength;
i++;
var box = new Uint8Array(u8.subarray(start, end));
// decrypt the chunk
var plaintext = Nacl.secretbox.open(box, nonce, key);
increment(nonce);
return plaintext;
}; };
var chunks = []; var chunks = [];
// decrypt file contents // decrypt file contents
var chunk;
for (;i * cypherChunkLength < u8.length;) { for (;i * cypherChunkLength < u8.length;) {
chunk = takeChunk(); chunk = takeChunk();
if (!chunk) { if (!chunk) {
@ -139,15 +123,12 @@ define([
var encrypt = function (u8, metadata, key) { var encrypt = function (u8, metadata, key) {
var nonce = createNonce(); var nonce = createNonce();
encodePrefix(); // TODO
// encode metadata // encode metadata
var metaBuffer = Array.prototype.slice var metaBuffer = Array.prototype.slice
.call(Nacl.util.decodeUTF8(JSON.stringify(metadata))); .call(Nacl.util.decodeUTF8(JSON.stringify(metadata)));
var plaintext = new Uint8Array(padChunk(metaBuffer)); var plaintext = new Uint8Array(metaBuffer);
var j = 0;
var i = 0; var i = 0;
/* /*
@ -164,22 +145,21 @@ define([
var part; var part;
var box; var box;
if (state === 0) { // metadata... // DONE
start = j * plainChunkLength; if (state === 2) { return void cb(); }
end = start + plainChunkLength;
part = plaintext.subarray(start, end); if (state === 0) { // metadata...
part = new Uint8Array(plaintext);
box = Nacl.secretbox(part, nonce, key); box = Nacl.secretbox(part, nonce, key);
increment(nonce); increment(nonce);
j++; if (box.length > 65535) {
return void cb('METADATA_TOO_LARGE');
// metadata is done
if (j * plainChunkLength >= plaintext.length) {
return void cb(state++, box);
} }
var prefixed = new Uint8Array(encodePrefix(box.length)
return void cb(state, box); .concat(slice(box)));
state++;
return void cb(void 0, prefixed);
} }
// encrypt the rest of the file... // encrypt the rest of the file...
@ -194,7 +174,7 @@ define([
// regular data is done // regular data is done
if (i * plainChunkLength >= u8.length) { state = 2; } if (i * plainChunkLength >= u8.length) { state = 2; }
return void cb(state, box); return void cb(void 0, box);
}; };
return next; return next;

@ -47,23 +47,16 @@ define([
}); });
}; };
var again = function (state, box) { var again = function (err, box) {
switch (state) { if (err) { throw new Error(err); }
case 0: if (box) {
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);
}); });
break; }
case 1:
sendChunk(box, function (e) { // if not box then done
if (e) { return console.error(e); }
next(again);
});
break;
case 2:
sendChunk(box, function (e) {
if (e) { return console.error(e); }
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); }
var id = res[0]; var id = res[0];
@ -91,11 +84,6 @@ define([
Cryptpad.alert("successfully uploaded: " + title); Cryptpad.alert("successfully uploaded: " + title);
}); });
}); });
});
break;
default:
throw new Error("E_INVAL_STATE");
}
}; };
Cryptpad.rpc.send('UPLOAD_STATUS', '', function (e, pending) { Cryptpad.rpc.send('UPLOAD_STATUS', '', function (e, pending) {

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html class="cp pad">
<head>
<title>CryptPad</title>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/bower_components/components-font-awesome/css/font-awesome.min.css">
<script data-bootload="main.js" data-main="/common/boot.js" src="/bower_components/requirejs/require.js"></script>
<link rel="icon" type="image/png"
href="/customize/main-favicon.png"
data-main-favicon="/customize/main-favicon.png"
data-alt-favicon="/customize/alt-favicon.png"
id="favicon" />
<link rel="stylesheet" href="/customize/main.css" />
</head>
<body>

@ -0,0 +1,87 @@
define([
'jquery',
'/bower_components/chainpad-crypto/crypto.js',
'/bower_components/chainpad-netflux/chainpad-netflux.js',
'/common/toolbar.js',
'/common/cryptpad-common.js',
'/common/visible.js',
'/common/notify.js',
'/file/file-crypto.js',
'/bower_components/tweetnacl/nacl-fast.min.js',
'/bower_components/file-saver/FileSaver.min.js',
], function ($, Crypto, realtimeInput, Toolbar, Cryptpad, Visible, Notify, FileCrypto) {
var Nacl = window.nacl;
$(function () {
var filesAreSame = function (a, b) {
var l = a.length;
if (l !== b.length) { return false; }
var i = 0;
for (; i < l; i++) { if (a[i] !== b[i]) { return false; } }
return true;
};
var metadataIsSame = function (A, B) {
return !Object.keys(A).some(function (k) {
return A[k] !== B[k];
});
};
var upload = function (blob, metadata) {
var u8 = new Uint8Array(blob);
var key = Nacl.randomBytes(32);
var next = FileCrypto.encrypt(u8, metadata, key);
var chunks = [];
var sendChunk = function (box, cb) {
chunks.push(box);
cb();
};
var again = function (err, box) {
if (err) { throw new Error(err); }
if (box) {
return void sendChunk(box, function (e) {
if (e) {
console.error(e);
return Cryptpad.alert('Something went wrong');
}
next(again);
});
}
// check if the uploaded file can be decrypted
var newU8 = FileCrypto.joinChunks(chunks);
console.log('encrypted file with metadata is %s uint8s', newU8.length);
FileCrypto.decrypt(newU8, key, function (e, res) {
if (e) { return Cryptpad.alert(e); }
if (filesAreSame(blob, res.content) &&
metadataIsSame(res.metadata, metadata)) {
Cryptpad.alert("successfully uploaded");
} else {
Cryptpad.alert('encryption failure!');
}
});
};
next(again);
};
var andThen = function () {
var src = '/customize/cryptofist_mini.png';
Cryptpad.fetch(src, function (e, file) {
console.log('original file is %s uint8s', file.length);
upload(file, {
pew: 'pew',
bang: 'bang',
});
});
};
andThen();
});
});
Loading…
Cancel
Save