move to chainpad version 2

This commit is contained in:
ansuz 2016-05-20 16:34:55 +02:00
parent 297d8c2d44
commit 976a08cc7a
4 changed files with 118 additions and 312 deletions

View File

@ -435,38 +435,20 @@ var REGISTER = Message.REGISTER = 0;
var REGISTER_ACK = Message.REGISTER_ACK = 1; var REGISTER_ACK = Message.REGISTER_ACK = 1;
var PATCH = Message.PATCH = 2; var PATCH = Message.PATCH = 2;
var DISCONNECT = Message.DISCONNECT = 3; var DISCONNECT = Message.DISCONNECT = 3;
var PING = Message.PING = 4;
var PONG = Message.PONG = 5;
var check = Message.check = function(msg) { var check = Message.check = function(msg) {
Common.assert(msg.type === 'Message'); Common.assert(msg.type === 'Message');
Common.assert(typeof(msg.userName) === 'string');
Common.assert(typeof(msg.authToken) === 'string');
Common.assert(typeof(msg.channelId) === 'string');
if (msg.messageType === PATCH) { if (msg.messageType === PATCH) {
Patch.check(msg.content); Patch.check(msg.content);
Common.assert(typeof(msg.lastMsgHash) === 'string'); Common.assert(typeof(msg.lastMsgHash) === 'string');
} else if (msg.messageType === PING || msg.messageType === PONG) {
Common.assert(typeof(msg.lastMsgHash) === 'undefined');
Common.assert(typeof(msg.content) === 'number');
} else if (msg.messageType === REGISTER
|| msg.messageType === REGISTER_ACK
|| msg.messageType === DISCONNECT)
{
Common.assert(typeof(msg.lastMsgHash) === 'undefined');
Common.assert(typeof(msg.content) === 'undefined');
} else { } else {
throw new Error("invalid message type [" + msg.messageType + "]"); throw new Error("invalid message type [" + msg.messageType + "]");
} }
}; };
var create = Message.create = function (userName, authToken, channelId, type, content, lastMsgHash) { var create = Message.create = function (type, content, lastMsgHash) {
var msg = { var msg = {
type: 'Message', type: 'Message',
userName: userName,
authToken: authToken,
channelId: channelId,
messageType: type, messageType: type,
content: content, content: content,
lastMsgHash: lastMsgHash lastMsgHash: lastMsgHash
@ -477,62 +459,67 @@ var create = Message.create = function (userName, authToken, channelId, type, co
var toString = Message.toString = function (msg) { var toString = Message.toString = function (msg) {
if (Common.PARANOIA) { check(msg); } if (Common.PARANOIA) { check(msg); }
var prefix = msg.messageType + ':';
var content = ''; if (msg.messageType === PATCH) {
if (msg.messageType === REGISTER) { return JSON.stringify([PATCH, Patch.toObj(msg.content), msg.lastMsgHash]);
content = JSON.stringify([REGISTER]); } else {
} else if (msg.messageType === PING || msg.messageType === PONG) { throw new Error();
content = JSON.stringify([msg.messageType, msg.content]);
} else if (msg.messageType === PATCH) {
content = JSON.stringify([PATCH, Patch.toObj(msg.content), msg.lastMsgHash]);
} }
return msg.authToken.length + ":" + msg.authToken + };
msg.userName.length + ":" + msg.userName +
msg.channelId.length + ":" + msg.channelId + var discardBencode = function (msg, arr) {
content.length + ':' + content; var len = msg.substring(0,msg.indexOf(':'));
msg = msg.substring(len.length+1);
var value = msg.substring(0,Number(len));
msg = msg.substring(value.length);
if (arr) { arr.push(value); }
return msg;
}; };
var fromString = Message.fromString = function (str) { var fromString = Message.fromString = function (str) {
var msg = str; var msg = str;
var unameLen = msg.substring(0,msg.indexOf(':')); if (str.charAt(0) === '[') {
msg = msg.substring(unameLen.length+1); var m = JSON.parse(str);
var userName = msg.substring(0,Number(unameLen)); return create(m[0], Patch.fromObj(m[1]), m[2]);
msg = msg.substring(userName.length);
var channelIdLen = msg.substring(0,msg.indexOf(':'));
msg = msg.substring(channelIdLen.length+1);
var channelId = msg.substring(0,Number(channelIdLen));
msg = msg.substring(channelId.length);
var contentStrLen = msg.substring(0,msg.indexOf(':'));
msg = msg.substring(contentStrLen.length+1);
var contentStr = msg.substring(0,Number(contentStrLen));
Common.assert(contentStr.length === Number(contentStrLen));
var content = JSON.parse(contentStr);
var message;
if (content[0] === PATCH) {
message = create(userName, '', channelId, PATCH, Patch.fromObj(content[1]), content[2]);
} else if (content[0] === PING || content[0] === PONG) {
message = create(userName, '', channelId, content[0], content[1]);
} else { } else {
message = create(userName, '', channelId, content[0]); /* Just in case we receive messages in the old format,
we should try to parse them. We only need the content, though,
so just extract that and throw the rest away */
var last;
var parts = [];
// chop off all the bencoded components
while (msg) {
msg = discardBencode(msg, parts);
}
// grab the last component from the parts
// we don't need anything else
var contentStr = parts.slice(-1)[0];
var content = JSON.parse(contentStr);
var message;
if (content[0] === PATCH) {
message = create(userName, PATCH, Patch.fromObj(content[1]), content[2]);
} else if ([4,5].indexOf(content[0]) !== -1 /* === PING || content[0] === PONG*/) {
// it's a ping or pong, which we don't want to support anymore
message = create(userName, content[0], content[1]);
} else {
message = create(userName, content[0]);
}
// This check validates every operation in the patch.
check(message);
return message
} }
// This check validates every operation in the patch.
check(message);
return message
}; };
var hashOf = Message.hashOf = function (msg) { var hashOf = Message.hashOf = function (msg) {
if (Common.PARANOIA) { check(msg); } if (Common.PARANOIA) { check(msg); }
var authToken = msg.authToken;
msg.authToken = '';
var hash = Sha.hex_sha256(toString(msg)); var hash = Sha.hex_sha256(toString(msg));
msg.authToken = authToken;
return hash; return hash;
}; };
@ -554,10 +541,10 @@ var hashOf = Message.hashOf = function (msg) {
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
var Common = require('./Common'); var Common = module.exports.Common = require('./Common');
var Operation = module.exports.Operation = require('./Operation'); var Operation = module.exports.Operation = require('./Operation');
var Patch = require('./Patch'); var Patch = module.exports.Patch = require('./Patch');
var Message = require('./Message'); var Message = module.exports.Message = require('./Message');
var Sha = module.exports.Sha = require('./SHA256'); var Sha = module.exports.Sha = require('./SHA256');
var ChainPad = {}; var ChainPad = {};
@ -634,12 +621,7 @@ var sync = function (realtime) {
if (realtime.best === realtime.initialMessage) { if (realtime.best === realtime.initialMessage) {
msg = realtime.initialMessage; msg = realtime.initialMessage;
} else { } else {
msg = Message.create(realtime.userName, msg = Message.create(Message.PATCH, realtime.uncommitted, realtime.best.hashOf);
realtime.authToken,
realtime.channelId,
Message.PATCH,
realtime.uncommitted,
realtime.best.hashOf);
} }
var strMsg = Message.toString(msg); var strMsg = Message.toString(msg);
@ -647,6 +629,8 @@ var sync = function (realtime) {
onMessage(realtime, strMsg, function (err) { onMessage(realtime, strMsg, function (err) {
if (err) { if (err) {
debug(realtime, "Posting to server failed [" + err + "]"); debug(realtime, "Posting to server failed [" + err + "]");
} else {
handleMessage(realtime, strMsg, true);
} }
}); });
@ -670,46 +654,9 @@ var sync = function (realtime) {
if (Common.PARANOIA) { check(realtime); } if (Common.PARANOIA) { check(realtime); }
}; };
var getMessages = function (realtime) { var create = ChainPad.create = function (config) {
realtime.registered = true;
/*var to = schedule(realtime, function () {
throw new Error("failed to connect to the server");
}, 5000);*/
var msg = Message.create(realtime.userName,
realtime.authToken,
realtime.channelId,
Message.REGISTER);
onMessage(realtime, Message.toString(msg), function (err) {
if (err) { throw err; }
});
};
var sendPing = function (realtime) {
realtime.pingSchedule = undefined;
realtime.lastPingTime = (new Date()).getTime();
var msg = Message.create(realtime.userName,
realtime.authToken,
realtime.channelId,
Message.PING,
realtime.lastPingTime);
onMessage(realtime, Message.toString(msg), function (err) {
if (err) { throw err; }
});
};
var onPong = function (realtime, msg) {
if (Common.PARANOIA) {
Common.assert(realtime.lastPingTime === Number(msg.content));
}
realtime.lastPingLag = (new Date()).getTime() - Number(msg.content);
realtime.lastPingTime = 0;
realtime.pingSchedule =
schedule(realtime, function () { sendPing(realtime); }, realtime.pingCycle);
};
var create = ChainPad.create = function (userName, authToken, channelId, initialState, config) {
config = config || {}; config = config || {};
var initialState = config.initialState || '';
var realtime = { var realtime = {
type: 'ChainPad', type: 'ChainPad',
@ -720,10 +667,6 @@ var create = ChainPad.create = function (userName, authToken, channelId, initial
logLevel: typeof(config.logLevel) !== 'undefined'? config.logLevel: 1, logLevel: typeof(config.logLevel) !== 'undefined'? config.logLevel: 1,
userName: userName,
authToken: authToken,
channelId: channelId,
/** A patch representing all uncommitted work. */ /** A patch representing all uncommitted work. */
uncommitted: null, uncommitted: null,
@ -755,23 +698,13 @@ var create = ChainPad.create = function (userName, authToken, channelId, initial
rootMessage: null, rootMessage: null,
userName: config.userName || 'anonymous',
/** /**
* Set to the message which sets the initialState if applicable. * Set to the message which sets the initialState if applicable.
* Reset to null after the initial message has been successfully broadcasted. * Reset to null after the initial message has been successfully broadcasted.
*/ */
initialMessage: null, initialMessage: null,
userListChangeHandlers: [],
userList: [],
/** The schedule() for sending pings. */
pingSchedule: undefined,
lastPingLag: 0,
lastPingTime: 0,
/** Average number of milliseconds between pings. */
pingCycle: 5000
}; };
if (Common.PARANOIA) { if (Common.PARANOIA) {
@ -781,7 +714,7 @@ var create = ChainPad.create = function (userName, authToken, channelId, initial
var zeroPatch = Patch.create(EMPTY_STR_HASH); var zeroPatch = Patch.create(EMPTY_STR_HASH);
zeroPatch.inverseOf = Patch.invert(zeroPatch, ''); zeroPatch.inverseOf = Patch.invert(zeroPatch, '');
zeroPatch.inverseOf.inverseOf = zeroPatch; zeroPatch.inverseOf.inverseOf = zeroPatch;
var zeroMsg = Message.create('', '', channelId, Message.PATCH, zeroPatch, ZERO); var zeroMsg = Message.create(Message.PATCH, zeroPatch, ZERO);
zeroMsg.hashOf = Message.hashOf(zeroMsg); zeroMsg.hashOf = Message.hashOf(zeroMsg);
zeroMsg.parentCount = 0; zeroMsg.parentCount = 0;
realtime.messages[zeroMsg.hashOf] = zeroMsg; realtime.messages[zeroMsg.hashOf] = zeroMsg;
@ -810,14 +743,10 @@ var create = ChainPad.create = function (userName, authToken, channelId, initial
if (Common.PARANOIA) { if (Common.PARANOIA) {
realtime.userInterfaceContent = initialState; realtime.userInterfaceContent = initialState;
} }
initialMessage = Message.create(realtime.userName, initialMessage = Message.create(Message.PATCH, initialStatePatch, zeroMsg.hashOf);
realtime.authToken,
realtime.channelId,
Message.PATCH,
initialStatePatch,
zeroMsg.hashOf);
initialMessage.hashOf = Message.hashOf(initialMessage); initialMessage.hashOf = Message.hashOf(initialMessage);
initialMessage.parentCount = 1; initialMessage.parentCount = 1;
initialMessage.isFromMe = true;
realtime.messages[initialMessage.hashOf] = initialMessage; realtime.messages[initialMessage.hashOf] = initialMessage;
(realtime.messagesByParent[initialMessage.lastMessageHash] || []).push(initialMessage); (realtime.messagesByParent[initialMessage.lastMessageHash] || []).push(initialMessage);
@ -887,8 +816,10 @@ var parentCount = function (realtime, message) {
return message.parentCount; return message.parentCount;
}; };
var applyPatch = function (realtime, author, patch) { var applyPatch = function (realtime, isFromMe, patch) {
if (author === realtime.userName && !patch.isInitialStatePatch) { Common.assert(patch);
Common.assert(patch.inverseOf);
if (isFromMe && !patch.isInitialStatePatch) {
var inverseOldUncommitted = Patch.invert(realtime.uncommitted, realtime.authDoc); var inverseOldUncommitted = Patch.invert(realtime.uncommitted, realtime.authDoc);
var userInterfaceContent = Patch.apply(realtime.uncommitted, realtime.authDoc); var userInterfaceContent = Patch.apply(realtime.uncommitted, realtime.authDoc);
if (Common.PARANOIA) { if (Common.PARANOIA) {
@ -907,12 +838,14 @@ var applyPatch = function (realtime, author, patch) {
realtime.authDoc = Patch.apply(patch, realtime.authDoc); realtime.authDoc = Patch.apply(patch, realtime.authDoc);
if (Common.PARANOIA) { if (Common.PARANOIA) {
Common.assert(realtime.uncommitted.parentHash === patch.inverseOf.parentHash);
Common.assert(Sha.hex_sha256(realtime.authDoc) === realtime.uncommitted.parentHash);
realtime.userInterfaceContent = Patch.apply(realtime.uncommitted, realtime.authDoc); realtime.userInterfaceContent = Patch.apply(realtime.uncommitted, realtime.authDoc);
} }
}; };
var revertPatch = function (realtime, author, patch) { var revertPatch = function (realtime, isFromMe, patch) {
applyPatch(realtime, author, patch.inverseOf); applyPatch(realtime, isFromMe, patch.inverseOf);
}; };
var getBestChild = function (realtime, msg) { var getBestChild = function (realtime, msg) {
@ -925,55 +858,23 @@ var getBestChild = function (realtime, msg) {
return best; return best;
}; };
var userListChange = function (realtime) { var handleMessage = ChainPad.handleMessage = function (realtime, msgStr, isFromMe) {
for (var i = 0; i < realtime.userListChangeHandlers.length; i++) {
var list = [];
list.push.apply(list, realtime.userList);
realtime.userListChangeHandlers[i](list);
}
};
var handleMessage = ChainPad.handleMessage = function (realtime, msgStr) {
if (Common.PARANOIA) { check(realtime); } if (Common.PARANOIA) { check(realtime); }
var msg = Message.fromString(msgStr); var msg = Message.fromString(msgStr);
Common.assert(msg.channelId === realtime.channelId);
if (msg.messageType === Message.REGISTER_ACK) { // These are all deprecated message types
debug(realtime, "registered"); if (['REGISTER', 'PONG', 'DISCONNECT'].map(function (x) {
realtime.registered = true; return Message[x];
sendPing(realtime); }).indexOf(msg.messageType) !== -1) {
return; console.log("Deprecated message type: [%s]", msg.messageType);
}
if (msg.messageType === Message.REGISTER) {
realtime.userList.push(msg.userName);
userListChange(realtime);
return;
}
if (msg.messageType === Message.PONG) {
onPong(realtime, msg);
return;
}
if (msg.messageType === Message.DISCONNECT) {
if (msg.userName === '') {
realtime.userList = [];
userListChange(realtime);
return;
}
var idx = realtime.userList.indexOf(msg.userName);
if (Common.PARANOIA) { Common.assert(idx > -1); }
if (idx > -1) {
realtime.userList.splice(idx, 1);
userListChange(realtime);
}
return; return;
} }
// otherwise it's a disconnect. // otherwise it's a disconnect.
if (msg.messageType !== Message.PATCH) { return; } if (msg.messageType !== Message.PATCH) {
console.error("disconnect");
return; }
msg.hashOf = Message.hashOf(msg); msg.hashOf = Message.hashOf(msg);
@ -1002,6 +903,7 @@ var handleMessage = ChainPad.handleMessage = function (realtime, msgStr) {
// of this message fills in a hole in the chain which makes another patch better, swap to the // of this message fills in a hole in the chain which makes another patch better, swap to the
// best child of this patch since longest chain always wins. // best child of this patch since longest chain always wins.
msg = getBestChild(realtime, msg); msg = getBestChild(realtime, msg);
msg.isFromMe = isFromMe;
var patch = msg.content; var patch = msg.content;
// Find the ancestor of this patch which is in the main chain, reverting as necessary // Find the ancestor of this patch which is in the main chain, reverting as necessary
@ -1040,6 +942,7 @@ var handleMessage = ChainPad.handleMessage = function (realtime, msgStr) {
var authDocAtTimeOfPatch = realtime.authDoc; var authDocAtTimeOfPatch = realtime.authDoc;
for (var i = 0; i < toRevert.length; i++) { for (var i = 0; i < toRevert.length; i++) {
Common.assert(typeof(toRevert[i].content.inverseOf) !== 'undefined');
authDocAtTimeOfPatch = Patch.apply(toRevert[i].content.inverseOf, authDocAtTimeOfPatch); authDocAtTimeOfPatch = Patch.apply(toRevert[i].content.inverseOf, authDocAtTimeOfPatch);
} }
@ -1086,13 +989,13 @@ var handleMessage = ChainPad.handleMessage = function (realtime, msgStr) {
for (var i = 0; i < toRevert.length; i++) { for (var i = 0; i < toRevert.length; i++) {
debug(realtime, "reverting [" + toRevert[i].hashOf + "]"); debug(realtime, "reverting [" + toRevert[i].hashOf + "]");
uncommittedPatch = Patch.merge(uncommittedPatch, toRevert[i].content.inverseOf); uncommittedPatch = Patch.merge(uncommittedPatch, toRevert[i].content.inverseOf);
revertPatch(realtime, toRevert[i].userName, toRevert[i].content); revertPatch(realtime, toRevert[i].isFromMe, toRevert[i].content);
} }
for (var i = 0; i < toApply.length; i++) { for (var i = 0; i < toApply.length; i++) {
debug(realtime, "applying [" + toApply[i].hashOf + "]"); debug(realtime, "applying [" + toApply[i].hashOf + "]");
uncommittedPatch = Patch.merge(uncommittedPatch, toApply[i].content); uncommittedPatch = Patch.merge(uncommittedPatch, toApply[i].content);
applyPatch(realtime, toApply[i].userName, toApply[i].content); applyPatch(realtime, toApply[i].isFromMe, toApply[i].content);
} }
uncommittedPatch = Patch.merge(uncommittedPatch, realtime.uncommitted); uncommittedPatch = Patch.merge(uncommittedPatch, realtime.uncommitted);
@ -1125,22 +1028,6 @@ var handleMessage = ChainPad.handleMessage = function (realtime, msgStr) {
if (Common.PARANOIA) { check(realtime); } if (Common.PARANOIA) { check(realtime); }
}; };
var wasEverState = function (content, realtime) {
Common.assert(typeof(content) === 'string');
// without this we would never get true on the ^HEAD
if (realtime.authDoc === content) {
return true;
}
var hash = Sha.hex_sha256(content);
var patchMsg = realtime.best;
do {
if (patchMsg.content.parentHash === hash) { return true; }
} while ((patchMsg = getParent(realtime, patchMsg)));
return false;
};
var getDepthOfState = function (content, minDepth, realtime) { var getDepthOfState = function (content, minDepth, realtime) {
Common.assert(typeof(content) === 'string'); Common.assert(typeof(content) === 'string');
@ -1169,47 +1056,29 @@ var getDepthOfState = function (content, minDepth, realtime) {
} }
depth++; depth++;
} while ((patchMsg = getParent(realtime, patchMsg))); } while ((patchMsg = getParent(realtime, patchMsg)));
return; return -1;
}; };
module.exports.create = function (userName, authToken, channelId, initialState, conf) { module.exports.create = function (conf) {
Common.assert(typeof(userName) === 'string'); var realtime = ChainPad.create(conf);
Common.assert(typeof(authToken) === 'string');
Common.assert(typeof(channelId) === 'string');
Common.assert(typeof(initialState) === 'string');
var realtime = ChainPad.create(userName, authToken, channelId, initialState, conf);
return { return {
onPatch: enterChainPad(realtime, function (handler) { onPatch: enterChainPad(realtime, function (handler) {
Common.assert(typeof(handler) === 'function'); Common.assert(typeof(handler) === 'function');
realtime.patchHandlers.push(handler); realtime.patchHandlers.push(handler);
}), }),
onRemove: enterChainPad(realtime, function (handler) {
Common.assert(typeof(handler) === 'function'); patch: enterChainPad(realtime, function (offset, count, chars) {
realtime.opHandlers.unshift(function (op) { doOperation(realtime, Operation.create(offset, count, chars));
if (op.toRemove > 0) { handler(op.offset, op.toRemove); }
});
}),
onInsert: enterChainPad(realtime, function (handler) {
Common.assert(typeof(handler) === 'function');
realtime.opHandlers.push(function (op) {
if (op.toInsert.length > 0) { handler(op.offset, op.toInsert); }
});
}),
remove: enterChainPad(realtime, function (offset, numChars) {
doOperation(realtime, Operation.create(offset, numChars, ''));
}),
insert: enterChainPad(realtime, function (offset, str) {
doOperation(realtime, Operation.create(offset, 0, str));
}), }),
onMessage: enterChainPad(realtime, function (handler) { onMessage: enterChainPad(realtime, function (handler) {
Common.assert(typeof(handler) === 'function'); Common.assert(typeof(handler) === 'function');
realtime.messageHandlers.push(handler); realtime.messageHandlers.push(handler);
}), }),
message: enterChainPad(realtime, function (message) { message: enterChainPad(realtime, function (message) {
handleMessage(realtime, message); handleMessage(realtime, message, false);
}), }),
start: enterChainPad(realtime, function () { start: enterChainPad(realtime, function () {
getMessages(realtime);
if (realtime.syncSchedule) { unschedule(realtime, realtime.syncSchedule); } if (realtime.syncSchedule) { unschedule(realtime, realtime.syncSchedule); }
realtime.syncSchedule = schedule(realtime, function () { sync(realtime); }); realtime.syncSchedule = schedule(realtime, function () { sync(realtime); });
}), }),
@ -1221,19 +1090,7 @@ module.exports.create = function (userName, authToken, channelId, initialState,
}), }),
getAuthDoc: function () { return realtime.authDoc; }, getAuthDoc: function () { return realtime.authDoc; },
getUserDoc: function () { return Patch.apply(realtime.uncommitted, realtime.authDoc); }, getUserDoc: function () { return Patch.apply(realtime.uncommitted, realtime.authDoc); },
onUserListChange: enterChainPad(realtime, function (handler) {
Common.assert(typeof(handler) === 'function');
realtime.userListChangeHandlers.push(handler);
}),
getLag: function () {
if (realtime.lastPingTime) {
return { waiting:1, lag: (new Date()).getTime() - realtime.lastPingTime };
}
return { waiting:0, lag: realtime.lastPingLag };
},
wasEverState: function (content) {
return wasEverState(content, realtime);
},
getDepthOfState: function (content, minDepth) { getDepthOfState: function (content, minDepth) {
return getDepthOfState(content, minDepth, realtime); return getDepthOfState(content, minDepth, realtime);
} }

View File

@ -22,36 +22,12 @@ define([
return Nacl.util.encodeUTF8(unpacked); return Nacl.util.encodeUTF8(unpacked);
}; };
// this is crap because of bencoding messages... it should go away....
var splitMessage = function (msg, sending) {
var idx = 0;
var nl;
for (var i = ((sending) ? 0 : 1); i < 3; i++) {
nl = msg.indexOf(':',idx);
idx = nl + Number(msg.substring(idx,nl)) + 1;
}
return [ msg.substring(0,idx), msg.substring(msg.indexOf(':',idx) + 1) ];
};
var encrypt = module.exports.encrypt = function (msg, key) { var encrypt = module.exports.encrypt = function (msg, key) {
var spl = splitMessage(msg, true); return encryptStr(msg, key);
var json = JSON.parse(spl[1]);
// non-patches are not encrypted.
if (json[0] !== 2) { return msg; }
json[1] = encryptStr(JSON.stringify(json[1]), key);
var res = JSON.stringify(json);
return spl[0] + res.length + ':' + res;
}; };
var decrypt = module.exports.decrypt = function (msg, key) { var decrypt = module.exports.decrypt = function (msg, key) {
var spl = splitMessage(msg, false); return decryptStr(msg, key);
var json = JSON.parse(spl[1]);
// non-patches are not encrypted.
if (json[0] !== 2) { return msg; }
if (typeof(json[1]) !== 'string') { throw new Error(); }
json[1] = JSON.parse(decryptStr(json[1], key));
var res = JSON.stringify(json);
return spl[0] + res.length + ':' + res;
}; };
var parseKey = module.exports.parseKey = function (str) { var parseKey = module.exports.parseKey = function (str) {

View File

@ -288,4 +288,4 @@ define(function () {
}; };
return { connect: connect }; return { connect: connect };
}); });

View File

@ -37,6 +37,8 @@ define([
verbose = function (x) { console.log(x); }; verbose = function (x) { console.log(x); };
verbose = function () {}; // comment out to enable verbose logging verbose = function () {}; // comment out to enable verbose logging
var unBencode = function (str) { return str.replace(/^\d+:/, ''); };
var start = module.exports.start = var start = module.exports.start =
function (config) function (config)
{ {
@ -59,27 +61,7 @@ define([
var realtime; var realtime;
var parseMessage = function (msg) { var parseMessage = function (msg) {
var res ={}; return unBencode(msg);//.slice(msg.indexOf(':[') + 1);
// two or more? use a for
['pass','user','channelId','content'].forEach(function(attr){
var len=msg.slice(0,msg.indexOf(':')),
// taking an offset lets us slice out the prop
// and saves us one string copy
o=len.length+1,
prop=res[attr]=msg.slice(o,Number(len)+o);
// slice off the property and its descriptor
msg = msg.slice(prop.length+o);
});
// content is the only attribute that's not a string
res.content=JSON.parse(res.content);
return res;
};
var mkMessage = function (user, chan, content) {
content = JSON.stringify(content);
return user.length + ':' + user +
chan.length + ':' + chan +
content.length + ':' + content;
}; };
var userList = { var userList = {
@ -137,6 +119,12 @@ define([
config.onLocal(); config.onLocal();
} }
} }
// slice off the bencoded header
// Why are we getting bencoded stuff to begin with?
// FIXME this shouldn't be necessary
message = unBencode(message);//.slice(message.indexOf(':[') + 1);
// pass the message into Chainpad // pass the message into Chainpad
realtime.message(message); realtime.message(message);
}; };
@ -154,10 +142,7 @@ define([
// shim between chainpad and netflux // shim between chainpad and netflux
chainpadAdapter = { chainpadAdapter = {
msgIn : function(peerId, msg) { msgIn : function(peerId, msg) {
var parsed = parseMessage(msg); var message = parseMessage(msg);
// Remove the password from the message
var passLen = msg.substring(0,msg.indexOf(':'));
var message = msg.substring(passLen.length+1 + Number(passLen));
try { try {
var decryptedMsg = Crypto.decrypt(message, cryptKey); var decryptedMsg = Crypto.decrypt(message, cryptKey);
messagesHistory.push(decryptedMsg); messagesHistory.push(decryptedMsg);
@ -166,33 +151,24 @@ define([
console.error(err); console.error(err);
return message; return message;
} }
}, },
msgOut : function(msg, wc) { msgOut : function(msg, wc) {
var parsed = parseMessage(msg); try {
if(parsed.content[0] === 0) { // We're registering : send a REGISTER_ACK to Chainpad return Crypto.encrypt(msg, cryptKey);
onMessage('', '1:y'+mkMessage('', channel, [1,0])); } catch (err) {
return; console.log(msg);
throw err;
} }
if(parsed.content[0] === 4) { // PING message from Chainpad
parsed.content[0] = 5;
onMessage('', '1:y'+mkMessage(parsed.user, parsed.channelId, parsed.content));
// wc.sendPing();
return;
}
return Crypto.encrypt(msg, cryptKey);
} }
}; };
var createRealtime = function(chan) { var createRealtime = function(chan) {
return ChainPad.create(userName, return ChainPad.create({
passwd, userName: userName,
channel, initialState: config.initialState,
config.initialState || '', transformFunction: config.transformFunction,
{ logLevel: typeof(config.logLevel) !== 'undefined'? config.logLevel : 1
transformFunction: config.transformFunction, });
logLevel: typeof(config.logLevel) !== 'undefined'? config.logLevel : 1
});
}; };
@ -225,13 +201,12 @@ define([
} }
// Sending a message... // Sending a message...
realtime.onMessage(function(message) { realtime.onMessage(function(message, cb) {
// Filter messages sent by Chainpad to make it compatible with Netflux // Filter messages sent by Chainpad to make it compatible with Netflux
message = chainpadAdapter.msgOut(message, wc); message = chainpadAdapter.msgOut(message, wc);
if(message) { if(message) {
wc.bcast(message).then(function() { wc.bcast(message).then(function() {
// Send the message back to Chainpad once it is sent to the recipients. cb();
onMessage(wc.myID, message);
}, function(err) { }, function(err) {
// The message has not been sent, display the error. // The message has not been sent, display the error.
console.error(err); console.error(err);
@ -283,8 +258,6 @@ define([
// pass messages that come out of netflux into our local handler // pass messages that come out of netflux into our local handler
network.on('disconnect', function (evt) { network.on('disconnect', function (evt) {
// TODO also abort if Netflux times out
// that will be managed in Netflux-client.js
if (config.onAbort) { if (config.onAbort) {
config.onAbort({ config.onAbort({
reason: evt.reason reason: evt.reason