Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging
This commit is contained in:
commit
b76dcba1d6
92
rpc.js
92
rpc.js
@ -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,14 +246,32 @@ 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 (typeof(store.getChannelSize) !== 'function') {
|
|
||||||
return cb('GET_CHANNEL_SIZE_UNSUPPORTED');
|
if (channel.length === 32) {
|
||||||
|
if (typeof(store.getChannelSize) !== 'function') {
|
||||||
|
return cb('GET_CHANNEL_SIZE_UNSUPPORTED');
|
||||||
|
}
|
||||||
|
|
||||||
|
return void store.getChannelSize(channel, function (e, size) {
|
||||||
|
if (e) { return void cb(e.code); }
|
||||||
|
cb(void 0, size);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return void store.getChannelSize(channel, function (e, size) {
|
// 'channel' refers to a file, so you need anoter API
|
||||||
if (e) { return void cb(e.code); }
|
getUploadSize(null, channel, function (e, size) {
|
||||||
|
if (e) { return void cb(e); }
|
||||||
cb(void 0, size);
|
cb(void 0, size);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -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,26 +15,40 @@ 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++;
|
||||||
} else {
|
cb();
|
||||||
failed = true;
|
} else {
|
||||||
failedOn = assertions;
|
failed = true;
|
||||||
failMessages.push({
|
failedOn = assertions;
|
||||||
test: i,
|
cb({
|
||||||
message: msg,
|
test: i,
|
||||||
output: returned,
|
message: msg,
|
||||||
});
|
output: result,
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -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) {
|
||||||
@ -194,7 +218,7 @@ define([
|
|||||||
return str || '';
|
return str || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
var formatFailures = function () {
|
var formatFailures = function () {
|
||||||
var template = multiline(function () { /*
|
var template = multiline(function () { /*
|
||||||
<p class="error">
|
<p class="error">
|
||||||
Failed on test number {{test}} with error message:
|
Failed on test number {{test}} with error message:
|
||||||
@ -215,16 +239,15 @@ 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,
|
totalAssertions: ASSERTS.length,
|
||||||
totalAssertions: ASSERTS.length,
|
passedAssertions: assertions,
|
||||||
passedAssertions: assertions,
|
plural: (assertions === 1? '' : 's'),
|
||||||
plural: (assertions === 1? '' : 's'),
|
failMessages: formatFailures()
|
||||||
failMessages: formatFailures()
|
};
|
||||||
};
|
|
||||||
|
|
||||||
var SUCCESS = swap(multiline(function(){/*
|
var SUCCESS = swap(multiline(function(){/*
|
||||||
<div class="report">{{passedAssertions}} / {{totalAssertions}} test{{plural}} passed.
|
<div class="report">{{passedAssertions}} / {{totalAssertions}} test{{plural}} passed.
|
||||||
@ -237,12 +260,13 @@ The test returned:
|
|||||||
{{previous}}
|
{{previous}}
|
||||||
*/}), dict);
|
*/}), dict);
|
||||||
|
|
||||||
var report = SUCCESS;
|
var report = SUCCESS;
|
||||||
|
|
||||||
return report;
|
return report;
|
||||||
|
});
|
||||||
|
|
||||||
|
var $report = $('.report');
|
||||||
|
$report.addClass(failed?'failure':'success');
|
||||||
});
|
});
|
||||||
|
|
||||||
var $report = $('.report');
|
|
||||||
$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,28 +57,40 @@ define([
|
|||||||
}, []));
|
}, []));
|
||||||
};
|
};
|
||||||
|
|
||||||
var padChunk = function (A) {
|
|
||||||
var padding;
|
|
||||||
if (A.length === plainChunkLength) { return A; }
|
|
||||||
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 decrypt = function (u8, key, cb) {
|
||||||
|
var fail = function (e) {
|
||||||
|
cb(e || "DECRYPTION_ERROR");
|
||||||
|
};
|
||||||
|
|
||||||
var nonce = createNonce();
|
var nonce = createNonce();
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
decodePrefix([]); // TODO
|
var prefix = u8.subarray(0, 2);
|
||||||
|
var metadataLength = decodePrefix(prefix);
|
||||||
|
|
||||||
|
var res = {
|
||||||
|
metadata: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
var metaBox = new Uint8Array(u8.subarray(2, 2 + metadataLength));
|
||||||
|
|
||||||
|
var metaChunk = Nacl.secretbox.open(metaBox, nonce, key);
|
||||||
|
increment(nonce);
|
||||||
|
|
||||||
|
try {
|
||||||
|
res.metadata = JSON.parse(Nacl.util.encodeUTF8(metaChunk));
|
||||||
|
} catch (e) {
|
||||||
|
return fail('E_METADATA_DECRYPTION');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res.metadata) {
|
||||||
|
return void setTimeout(function () {
|
||||||
|
cb('NO_METADATA');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var takeChunk = function () {
|
var takeChunk = function () {
|
||||||
var start = i * cypherChunkLength;
|
var start = i * cypherChunkLength + 2 + metadataLength;
|
||||||
var end = start + cypherChunkLength;
|
var end = start + cypherChunkLength;
|
||||||
i++;
|
i++;
|
||||||
var box = new Uint8Array(u8.subarray(start, end));
|
var box = new Uint8Array(u8.subarray(start, end));
|
||||||
@ -89,37 +101,9 @@ define([
|
|||||||
return plaintext;
|
return plaintext;
|
||||||
};
|
};
|
||||||
|
|
||||||
var buffer = '';
|
|
||||||
|
|
||||||
var res = {
|
|
||||||
metadata: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
// decrypt metadata
|
|
||||||
var chunk;
|
|
||||||
for (; !res.metadata && i * cypherChunkLength < u8.length;) {
|
|
||||||
chunk = takeChunk();
|
|
||||||
buffer += Nacl.util.encodeUTF8(chunk);
|
|
||||||
try {
|
|
||||||
res.metadata = JSON.parse(buffer);
|
|
||||||
//console.log(res.metadata);
|
|
||||||
} catch (e) {
|
|
||||||
console.log('buffering another chunk for metadata');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!res.metadata) {
|
|
||||||
return void setTimeout(function () {
|
|
||||||
cb('NO_METADATA');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var fail = function () {
|
|
||||||
cb("DECRYPTION_ERROR");
|
|
||||||
};
|
|
||||||
|
|
||||||
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,55 +47,43 @@ 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 (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) {
|
|
||||||
if (e) { return void console.error(e); }
|
|
||||||
var id = res[0];
|
|
||||||
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);
|
|
||||||
window.location.hash = Cryptpad.getFileHashFromKeys(id, b64Key);
|
|
||||||
|
|
||||||
$form.hide();
|
|
||||||
|
|
||||||
APP.toolbar.addElement(['fileshare'], {});
|
|
||||||
|
|
||||||
// check if the uploaded file can be decrypted
|
|
||||||
var newU8 = FileCrypto.joinChunks(chunks);
|
|
||||||
FileCrypto.decrypt(newU8, key, function (e, res) {
|
|
||||||
if (e) { return console.error(e); }
|
|
||||||
var title = document.title = res.metadata.name;
|
|
||||||
myFile = res.content;
|
|
||||||
myDataType = res.metadata.type;
|
|
||||||
|
|
||||||
var defaultName = Cryptpad.getDefaultName(Cryptpad.parsePadUrl(window.location.href));
|
|
||||||
Title.updateTitle(title || defaultName);
|
|
||||||
APP.toolbar.title.show();
|
|
||||||
Cryptpad.alert("successfully uploaded: " + title);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("E_INVAL_STATE");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if not box then done
|
||||||
|
Cryptpad.rpc.send('UPLOAD_COMPLETE', '', function (e, res) {
|
||||||
|
if (e) { return void console.error(e); }
|
||||||
|
var id = res[0];
|
||||||
|
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);
|
||||||
|
window.location.hash = Cryptpad.getFileHashFromKeys(id, b64Key);
|
||||||
|
|
||||||
|
$form.hide();
|
||||||
|
|
||||||
|
APP.toolbar.addElement(['fileshare'], {});
|
||||||
|
|
||||||
|
// check if the uploaded file can be decrypted
|
||||||
|
var newU8 = FileCrypto.joinChunks(chunks);
|
||||||
|
FileCrypto.decrypt(newU8, key, function (e, res) {
|
||||||
|
if (e) { return console.error(e); }
|
||||||
|
var title = document.title = res.metadata.name;
|
||||||
|
myFile = res.content;
|
||||||
|
myDataType = res.metadata.type;
|
||||||
|
|
||||||
|
var defaultName = Cryptpad.getDefaultName(Cryptpad.parsePadUrl(window.location.href));
|
||||||
|
Title.updateTitle(title || defaultName);
|
||||||
|
APP.toolbar.title.show();
|
||||||
|
Cryptpad.alert("successfully uploaded: " + title);
|
||||||
|
});
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Cryptpad.rpc.send('UPLOAD_STATUS', '', function (e, pending) {
|
Cryptpad.rpc.send('UPLOAD_STATUS', '', function (e, pending) {
|
||||||
|
|||||||
16
www/file/test/index.html
Normal file
16
www/file/test/index.html
Normal file
@ -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>
|
||||||
87
www/file/test/main.js
Normal file
87
www/file/test/main.js
Normal file
@ -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…
x
Reference in New Issue
Block a user