Merge branch 'staging' of github.com:xwiki-labs/cryptpad into staging
commit
2411932229
|
@ -400,6 +400,11 @@ module.exports.create = function (cfg) {
|
|||
* writes messages to the store
|
||||
*/
|
||||
const onChannelMessage = function (ctx, channel, msgStruct) {
|
||||
// TODO our usage of 'channel' here looks prone to errors
|
||||
// we only use it for its 'id', but it can contain other stuff
|
||||
// also, we're using this RPC from both the RPC and Netflux-server
|
||||
// we should probably just change this to expect a channel id directly
|
||||
|
||||
// don't store messages if the channel id indicates that it's an ephemeral message
|
||||
if (!channel.id || channel.id.length === EPHEMERAL_CHANNEL_LENGTH) { return; }
|
||||
|
||||
|
@ -986,7 +991,6 @@ module.exports.create = function (cfg) {
|
|||
onChannelCleared(ctx, msg[4]);
|
||||
}
|
||||
|
||||
// FIXME METADATA CHANGE
|
||||
if (msg[3] === 'SET_METADATA') { // or whatever we call the RPC????
|
||||
// make sure we update our cache of metadata
|
||||
// or at least invalidate it and force other mechanisms to recompute its state
|
||||
|
@ -994,6 +998,12 @@ module.exports.create = function (cfg) {
|
|||
onChannelMetadataChanged(ctx, msg[4].channel, output[1]);
|
||||
}
|
||||
|
||||
// unauthenticated RPC calls have a different message format
|
||||
if (msg[0] === "WRITE_PRIVATE_MESSAGE" && output) {
|
||||
historyKeeperBroadcast(ctx, output.channel, output.message);
|
||||
}
|
||||
|
||||
// finally, send a response to the client that sent the RPC
|
||||
sendMsg(ctx, user, [0, HISTORY_KEEPER_ID, 'MSG', user.id, JSON.stringify([parsed[0]].concat(output))]);
|
||||
});
|
||||
} catch (e) {
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
var Netflux = require("netflux-websocket");
|
||||
var WebSocket = require("ws"); // jshint ignore:line
|
||||
var nThen = require("nthen");
|
||||
|
||||
var Util = require("../../www/common/common-util");
|
||||
var Rpc = require("../../www/common/rpc");
|
||||
|
||||
var Nacl = require("tweetnacl");
|
||||
|
||||
var makeKeys = function () {
|
||||
var keys = Nacl.sign.keyPair.fromSeed(Nacl.randomBytes(Nacl.sign.seedLength));
|
||||
return {
|
||||
secret: Nacl.util.encodeBase64(keys.secretKey),
|
||||
public: Nacl.util.encodeBase64(keys.publicKey),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var Client = module.exports;
|
||||
|
||||
var createNetwork = Client.createNetwork = function (url, cb) {
|
||||
var CB = Util.once(cb);
|
||||
|
||||
var info = {};
|
||||
|
||||
Netflux.connect(url, function (url) {
|
||||
info.websocket = new WebSocket(url)
|
||||
.on('error', function (err) {
|
||||
console.log(err);
|
||||
})
|
||||
.on('close', function (err) {
|
||||
console.log("close");
|
||||
console.log(err);
|
||||
});
|
||||
return info.websocket;
|
||||
}).then(function (network) {
|
||||
info.network = network;
|
||||
CB(void 0, info);
|
||||
}, function (err) {
|
||||
CB(err);
|
||||
});
|
||||
};
|
||||
|
||||
var die = function (client) {
|
||||
var disconnect = Util.find(client, ['config', 'network', 'disconnect']);
|
||||
if (typeof(disconnect) === 'function') {
|
||||
disconnect();
|
||||
} else {
|
||||
console.error("disconnect was not a function");
|
||||
}
|
||||
var close = Util.find(client, ['config', 'websocket', 'close']);
|
||||
if (typeof(close) === 'function') {
|
||||
client.config.websocket.close();
|
||||
} else {
|
||||
console.error("close was not a function");
|
||||
}
|
||||
};
|
||||
|
||||
Client.create = function (config, cb) {
|
||||
if (typeof(config) === 'function') {
|
||||
cb = config;
|
||||
config = {};
|
||||
}
|
||||
var client = {
|
||||
config: config,
|
||||
};
|
||||
var CB = Util.once(function (err, arg) {
|
||||
if (err) { die(client); }
|
||||
cb(err, arg);
|
||||
});
|
||||
|
||||
client.shutdown = function () {
|
||||
die(client);
|
||||
};
|
||||
|
||||
nThen(function (w) {
|
||||
if (config.network) { return; }
|
||||
// connect to the network...
|
||||
createNetwork('ws://localhost:3000/cryptpad_websocket', w(function (err, info) {
|
||||
//console.log(_network);
|
||||
config.network = info.network;
|
||||
config.websocket = info.websocket;
|
||||
}));
|
||||
}).nThen(function (w) {
|
||||
// make sure the network has a historyKeeper id on it
|
||||
// we're responsible for adding it
|
||||
if (config.network.historyKeeper) { return; }
|
||||
var channel = Util.uint8ArrayToHex(Nacl.randomBytes(16));
|
||||
config.network.join(channel).then(w(function (wc) {
|
||||
wc.members.some(function (member) {
|
||||
if (member.length !== 16) { return; }
|
||||
config.network.historyKeeper = member;
|
||||
return true;
|
||||
});
|
||||
wc.leave();
|
||||
}), function (err) {
|
||||
w.abort();
|
||||
CB(err);
|
||||
});
|
||||
}).nThen(function (w) {
|
||||
// connect to the anonRpc
|
||||
Rpc.createAnonymous(config.network, w(function (err, rpc) {
|
||||
if (err) {
|
||||
return void CB('ANON_RPC_CONNECT_ERR');
|
||||
}
|
||||
client.anonRpc = rpc;
|
||||
}));
|
||||
var keys = makeKeys();
|
||||
Rpc.create(config.network, keys.secret, keys.public, w(function (err, rpc) {
|
||||
if (err) {
|
||||
return void CB('RPC_CONNECT_ERR');
|
||||
}
|
||||
client.rpc = rpc;
|
||||
}));
|
||||
}).nThen(function () {
|
||||
CB(void 0, client);
|
||||
});
|
||||
};
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "cryptpad",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -800,6 +800,11 @@
|
|||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||
},
|
||||
"netflux-websocket": {
|
||||
"version": "0.1.20",
|
||||
"resolved": "https://registry.npmjs.org/netflux-websocket/-/netflux-websocket-0.1.20.tgz",
|
||||
"integrity": "sha512-svFkw4ol4gmkcXKnx5kF/8tR9mmtTCDzUlLy4mSlcNl/4iWlbDmgwp/+aJ3nqtv8fg12m+DAFGX2+fbC0//dcg=="
|
||||
},
|
||||
"nthen": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/nthen/-/nthen-0.1.8.tgz",
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
"chainpad-server": "~3.0.2",
|
||||
"express": "~4.16.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
"get-folder-size": "^2.0.1",
|
||||
"netflux-websocket": "^0.1.20",
|
||||
"nthen": "0.1.8",
|
||||
"pull-stream": "^3.6.1",
|
||||
"replify": "^1.2.0",
|
||||
|
@ -18,8 +20,7 @@
|
|||
"sortify": "^1.0.4",
|
||||
"stream-to-pull-stream": "^1.7.2",
|
||||
"tweetnacl": "~0.12.2",
|
||||
"ws": "^3.3.1",
|
||||
"get-folder-size": "^2.0.1"
|
||||
"ws": "^3.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"flow-bin": "^0.59.0",
|
||||
|
@ -38,6 +39,7 @@
|
|||
"lint:less": "./node_modules/lesshint/bin/lesshint -c ./.lesshintrc ./customize.dist/src/less2/",
|
||||
"flow": "./node_modules/.bin/flow",
|
||||
"test": "node scripts/TestSelenium.js",
|
||||
"test-rpc": "cd scripts && node test-rpc",
|
||||
"template": "cd customize.dist/src && for page in ../index.html ../privacy.html ../terms.html ../about.html ../contact.html ../what-is-cryptpad.html ../features.html ../../www/login/index.html ../../www/register/index.html ../../www/user/index.html;do echo $page; cp template.html $page; done;"
|
||||
}
|
||||
}
|
||||
|
|
56
rpc.js
56
rpc.js
|
@ -1555,6 +1555,57 @@ var isNewChannel = function (Env, channel, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
/* writePrivateMessage
|
||||
allows users to anonymously send a message to the channel
|
||||
prevents their netflux-id from being stored in history
|
||||
and from being broadcast to anyone that might currently be in the channel
|
||||
|
||||
Otherwise behaves the same as sending to a channel
|
||||
*/
|
||||
var writePrivateMessage = function (Env, args, nfwssCtx, cb) {
|
||||
var channelId = args[0];
|
||||
var msg = args[1];
|
||||
|
||||
// don't bother handling empty messages
|
||||
if (!msg) { return void cb("INVALID_MESSAGE"); }
|
||||
|
||||
// don't support anything except regular channels
|
||||
if (!isValidId(channelId) || channelId.length !== 32) {
|
||||
return void cb("INVALID_CHAN");
|
||||
}
|
||||
|
||||
// We expect a modern netflux-websocket-server instance
|
||||
// if this API isn't here everything will fall apart anyway
|
||||
if (!(nfwssCtx && nfwssCtx.historyKeeper && typeof(nfwssCtx.historyKeeper.onChannelMessage) === 'function')) {
|
||||
return void cb("NOT_IMPLEMENTED");
|
||||
}
|
||||
|
||||
// historyKeeper expects something with an 'id' attribute
|
||||
// it will fail unless you provide it, but it doesn't need anything else
|
||||
var channelStruct = {
|
||||
id: channelId,
|
||||
};
|
||||
|
||||
// construct a message to store and broadcast
|
||||
var fullMessage = [
|
||||
0, // idk
|
||||
null, // normally the netflux id, null isn't rejected, and it distinguishes messages written in this way
|
||||
"MSG", // indicate that this is a MSG
|
||||
channelId, // channel id
|
||||
msg // the actual message content. Generally a string
|
||||
];
|
||||
|
||||
// store the message and do everything else that is typically done when going through historyKeeper
|
||||
nfwssCtx.historyKeeper.onChannelMessage(nfwssCtx, channelStruct, fullMessage);
|
||||
|
||||
// call back with the message and the target channel.
|
||||
// historyKeeper will take care of broadcasting it if anyone is in the channel
|
||||
cb(void 0, {
|
||||
channel: channelId,
|
||||
message: fullMessage
|
||||
});
|
||||
};
|
||||
|
||||
var getDiskUsage = function (Env, cb) {
|
||||
var data = {};
|
||||
nThen(function (waitFor) {
|
||||
|
@ -1654,6 +1705,7 @@ var isUnauthenticatedCall = function (call) {
|
|||
'IS_NEW_CHANNEL',
|
||||
'GET_HISTORY_OFFSET',
|
||||
'GET_DELETED_PADS',
|
||||
'WRITE_PRIVATE_MESSAGE',
|
||||
].indexOf(call) !== -1;
|
||||
};
|
||||
|
||||
|
@ -1821,6 +1873,10 @@ RPC.create = function (
|
|||
return void isNewChannel(Env, msg[1], function (e, isNew) {
|
||||
respond(e, [null, isNew, null]);
|
||||
});
|
||||
case 'WRITE_PRIVATE_MESSAGE':
|
||||
return void writePrivateMessage(Env, msg[1], nfwssCtx, function (e, output) {
|
||||
respond(e, output);
|
||||
});
|
||||
default:
|
||||
Log.warn("UNSUPPORTED_RPC_CALL", msg);
|
||||
return respond('UNSUPPORTED_RPC_CALL', msg);
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/* globals process */
|
||||
var Client = require("../lib/client/");
|
||||
var Mailbox = require("../www/bower_components/chainpad-crypto").Mailbox;
|
||||
var Nacl = require("tweetnacl");
|
||||
|
||||
var makeKeys = function () {
|
||||
var pair = Nacl.box.keyPair();
|
||||
return {
|
||||
curvePrivate: Nacl.util.encodeBase64(pair.secretKey),
|
||||
curvePublic: Nacl.util.encodeBase64(pair.publicKey),
|
||||
};
|
||||
};
|
||||
|
||||
Client.create(function (err, client) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var channel = "d34ebe83931382fcad9fe2e2d0e2cb5f"; // channel
|
||||
var recipient = "e8jvf36S3chzkkcaMrLSW7PPrz7VDp85lIFNI26dTmw="; // curvePublic
|
||||
|
||||
var keys = makeKeys();
|
||||
var cryptor = Mailbox.createEncryptor(keys);
|
||||
|
||||
var message = cryptor.encrypt(JSON.stringify({
|
||||
type: "CHEESE",
|
||||
author: keys.curvePublic,
|
||||
content: {
|
||||
text: "CAMEMBERT",
|
||||
}
|
||||
}), recipient);
|
||||
|
||||
client.anonRpc.send('WRITE_PRIVATE_MESSAGE', [channel, message], function (err, response) {
|
||||
if (err) {
|
||||
return void console.error(err);
|
||||
}
|
||||
|
||||
response = response;
|
||||
// shutdown doesn't work, so we need to do this instead
|
||||
client.shutdown();
|
||||
});
|
||||
});
|
|
@ -1,7 +1,5 @@
|
|||
(function (window) {
|
||||
define([], function () {
|
||||
var Util = window.CryptPad_Util = {};
|
||||
|
||||
var Util = {};
|
||||
Util.mkAsync = function (f) {
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
|
@ -349,6 +347,14 @@ define([], function () {
|
|||
return false;
|
||||
};
|
||||
|
||||
return Util;
|
||||
});
|
||||
}(self));
|
||||
if (typeof(module) !== 'undefined' && module.exports) {
|
||||
module.exports = Util;
|
||||
} else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) {
|
||||
define([], function () {
|
||||
window.CryptPad_Util = Util;
|
||||
return Util;
|
||||
});
|
||||
} else {
|
||||
window.CryptPad_Util = Util;
|
||||
}
|
||||
}(typeof(self) !== 'undefined'? self: this));
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
define([
|
||||
'/common/common-util.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function (Util) {
|
||||
var Nacl = window.nacl;
|
||||
|
||||
(function () {
|
||||
var factory = function (Util, Nacl) {
|
||||
var uid = Util.uid;
|
||||
var signMsg = function (data, signKey) {
|
||||
var buffer = Nacl.util.decodeUTF8(JSON.stringify(data));
|
||||
|
@ -337,4 +333,18 @@ types of messages:
|
|||
};
|
||||
|
||||
return { create: create, createAnonymous: createAnonymous };
|
||||
});
|
||||
};
|
||||
|
||||
if (typeof(module) !== 'undefined' && module.exports) {
|
||||
module.exports = factory(require("./common-util"), require("tweetnacl"));
|
||||
} else if ((typeof(define) !== 'undefined' && define !== null) && (define.amd !== null)) {
|
||||
define([
|
||||
'/common/common-util.js',
|
||||
'/bower_components/tweetnacl/nacl-fast.min.js',
|
||||
], function (Util) {
|
||||
return factory(Util, window.Nacl);
|
||||
});
|
||||
} else {
|
||||
// I'm not gonna bother supporting any other kind of instanciation
|
||||
}
|
||||
}());
|
||||
|
|
Loading…
Reference in New Issue