Merge pull request #2038 from s1341/fix/server_not_checking_changeset_req_bounds
Server doesn't check bounds on CHANGESET_REQ arguments
This commit is contained in:
commit
d1cad04647
|
@ -1,6 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions
|
* The MessageHandler handles all Messages that comes from Socket.IO and controls the sessions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
|
||||||
|
@ -106,12 +106,12 @@ exports.kickSessionsFromPad = function(padID)
|
||||||
* @param client the client that leaves
|
* @param client the client that leaves
|
||||||
*/
|
*/
|
||||||
exports.handleDisconnect = function(client)
|
exports.handleDisconnect = function(client)
|
||||||
{
|
{
|
||||||
stats.meter('disconnects').mark();
|
stats.meter('disconnects').mark();
|
||||||
|
|
||||||
//save the padname of this session
|
//save the padname of this session
|
||||||
var session = sessioninfos[client.id];
|
var session = sessioninfos[client.id];
|
||||||
|
|
||||||
//if this connection was already etablished with a handshake, send a disconnect message to the others
|
//if this connection was already etablished with a handshake, send a disconnect message to the others
|
||||||
if(session && session.author)
|
if(session && session.author)
|
||||||
{
|
{
|
||||||
|
@ -128,7 +128,7 @@ exports.handleDisconnect = function(client)
|
||||||
authorManager.getAuthorColorId(session.author, function(err, color)
|
authorManager.getAuthorColorId(session.author, function(err, color)
|
||||||
{
|
{
|
||||||
ERR(err);
|
ERR(err);
|
||||||
|
|
||||||
//prepare the notification for the other users on the pad, that this user left
|
//prepare the notification for the other users on the pad, that this user left
|
||||||
var messageToTheOtherUsers = {
|
var messageToTheOtherUsers = {
|
||||||
"type": "COLLABROOM",
|
"type": "COLLABROOM",
|
||||||
|
@ -142,14 +142,14 @@ exports.handleDisconnect = function(client)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//Go trough all user that are still on the pad, and send them the USER_LEAVE message
|
//Go trough all user that are still on the pad, and send them the USER_LEAVE message
|
||||||
client.broadcast.to(session.padId).json.send(messageToTheOtherUsers);
|
client.broadcast.to(session.padId).json.send(messageToTheOtherUsers);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Delete the sessioninfos entrys of this session
|
//Delete the sessioninfos entrys of this session
|
||||||
delete sessioninfos[client.id];
|
delete sessioninfos[client.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -158,7 +158,7 @@ exports.handleDisconnect = function(client)
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
*/
|
*/
|
||||||
exports.handleMessage = function(client, message)
|
exports.handleMessage = function(client, message)
|
||||||
{
|
{
|
||||||
if(message == null)
|
if(message == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -174,7 +174,7 @@ exports.handleMessage = function(client, message)
|
||||||
|
|
||||||
var handleMessageHook = function(callback){
|
var handleMessageHook = function(callback){
|
||||||
var dropMessage = false;
|
var dropMessage = false;
|
||||||
// Call handleMessage hook. If a plugin returns null, the message will be dropped. Note that for all messages
|
// Call handleMessage hook. If a plugin returns null, the message will be dropped. Note that for all messages
|
||||||
// handleMessage will be called, even if the client is not authorized
|
// handleMessage will be called, even if the client is not authorized
|
||||||
hooks.aCallAll("handleMessage", { client: client, message: message }, function ( err, messages ) {
|
hooks.aCallAll("handleMessage", { client: client, message: message }, function ( err, messages ) {
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
@ -183,7 +183,7 @@ exports.handleMessage = function(client, message)
|
||||||
dropMessage = true;
|
dropMessage = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// If no plugins explicitly told us to drop the message, its ok to proceed
|
// If no plugins explicitly told us to drop the message, its ok to proceed
|
||||||
if(!dropMessage){ callback() };
|
if(!dropMessage){ callback() };
|
||||||
});
|
});
|
||||||
|
@ -259,7 +259,7 @@ exports.handleMessage = function(client, message)
|
||||||
var checkAccessCallback = function(err, statusObject)
|
var checkAccessCallback = function(err, statusObject)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//access was granted
|
//access was granted
|
||||||
if(statusObject.accessStatus == "grant")
|
if(statusObject.accessStatus == "grant")
|
||||||
{
|
{
|
||||||
|
@ -297,17 +297,17 @@ exports.handleMessage = function(client, message)
|
||||||
function handleSaveRevisionMessage(client, message){
|
function handleSaveRevisionMessage(client, message){
|
||||||
var padId = sessioninfos[client.id].padId;
|
var padId = sessioninfos[client.id].padId;
|
||||||
var userId = sessioninfos[client.id].author;
|
var userId = sessioninfos[client.id].author;
|
||||||
|
|
||||||
padManager.getPad(padId, function(err, pad)
|
padManager.getPad(padId, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err)) return;
|
if(ERR(err)) return;
|
||||||
|
|
||||||
pad.addSavedRevision(pad.head, userId);
|
pad.addSavedRevision(pad.head, userId);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a custom message, different to the function below as it handles objects not strings and you can
|
* Handles a custom message, different to the function below as it handles objects not strings and you can
|
||||||
* direct the message to specific sessionID
|
* direct the message to specific sessionID
|
||||||
*
|
*
|
||||||
* @param msg {Object} the message we're sending
|
* @param msg {Object} the message we're sending
|
||||||
|
@ -356,10 +356,10 @@ function handleChatMessage(client, message)
|
||||||
var userId = sessioninfos[client.id].author;
|
var userId = sessioninfos[client.id].author;
|
||||||
var text = message.data.text;
|
var text = message.data.text;
|
||||||
var padId = sessioninfos[client.id].padId;
|
var padId = sessioninfos[client.id].padId;
|
||||||
|
|
||||||
var pad;
|
var pad;
|
||||||
var userName;
|
var userName;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the pad
|
//get the pad
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -385,7 +385,7 @@ function handleChatMessage(client, message)
|
||||||
{
|
{
|
||||||
//save the chat message
|
//save the chat message
|
||||||
pad.appendChatMessage(text, userId, time);
|
pad.appendChatMessage(text, userId, time);
|
||||||
|
|
||||||
var msg = {
|
var msg = {
|
||||||
type: "COLLABROOM",
|
type: "COLLABROOM",
|
||||||
data: {
|
data: {
|
||||||
|
@ -396,10 +396,10 @@ function handleChatMessage(client, message)
|
||||||
text: text
|
text: text
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//broadcast the chat message to everyone on the pad
|
//broadcast the chat message to everyone on the pad
|
||||||
socketio.sockets.in(padId).json.send(msg);
|
socketio.sockets.in(padId).json.send(msg);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
|
@ -425,20 +425,20 @@ function handleGetChatMessages(client, message)
|
||||||
messageLogger.warn("Dropped message, GetChatMessages Message has no start!");
|
messageLogger.warn("Dropped message, GetChatMessages Message has no start!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var start = message.data.start;
|
var start = message.data.start;
|
||||||
var end = message.data.end;
|
var end = message.data.end;
|
||||||
var count = start - count;
|
var count = start - count;
|
||||||
|
|
||||||
if(count < 0 && count > 100)
|
if(count < 0 && count > 100)
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, GetChatMessages Message, client requested invalid amout of messages!");
|
messageLogger.warn("Dropped message, GetChatMessages Message, client requested invalid amout of messages!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var padId = sessioninfos[client.id].padId;
|
var padId = sessioninfos[client.id].padId;
|
||||||
var pad;
|
var pad;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the pad
|
//get the pad
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -488,10 +488,10 @@ function handleSuggestUserName(client, message)
|
||||||
messageLogger.warn("Dropped message, suggestUserName Message has no unnamedId!");
|
messageLogger.warn("Dropped message, suggestUserName Message has no unnamedId!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var padId = sessioninfos[client.id].padId,
|
var padId = sessioninfos[client.id].padId,
|
||||||
clients = socketio.sockets.clients(padId);
|
clients = socketio.sockets.clients(padId);
|
||||||
|
|
||||||
//search the author and send him this message
|
//search the author and send him this message
|
||||||
for(var i = 0; i < clients.length; i++) {
|
for(var i = 0; i < clients.length; i++) {
|
||||||
var session = sessioninfos[clients[i].id];
|
var session = sessioninfos[clients[i].id];
|
||||||
|
@ -520,14 +520,14 @@ function handleUserInfoUpdate(client, message)
|
||||||
messageLogger.warn("Dropped message, USERINFO_UPDATE Message has no colorId!");
|
messageLogger.warn("Dropped message, USERINFO_UPDATE Message has no colorId!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Find out the author name of this session
|
//Find out the author name of this session
|
||||||
var author = sessioninfos[client.id].author;
|
var author = sessioninfos[client.id].author;
|
||||||
|
|
||||||
//Tell the authorManager about the new attributes
|
//Tell the authorManager about the new attributes
|
||||||
authorManager.setAuthorColorId(author, message.data.userInfo.colorId);
|
authorManager.setAuthorColorId(author, message.data.userInfo.colorId);
|
||||||
authorManager.setAuthorName(author, message.data.userInfo.name);
|
authorManager.setAuthorName(author, message.data.userInfo.name);
|
||||||
|
|
||||||
var padId = sessioninfos[client.id].padId;
|
var padId = sessioninfos[client.id].padId;
|
||||||
|
|
||||||
var infoMsg = {
|
var infoMsg = {
|
||||||
|
@ -545,7 +545,7 @@ function handleUserInfoUpdate(client, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//Send the other clients on the pad the update message
|
//Send the other clients on the pad the update message
|
||||||
client.broadcast.to(padId).json.send(infoMsg);
|
client.broadcast.to(padId).json.send(infoMsg);
|
||||||
}
|
}
|
||||||
|
@ -588,7 +588,7 @@ function handleUserChanges(data, cb)
|
||||||
messageLogger.warn("Dropped message, USER_CHANGES Message has no changeset!");
|
messageLogger.warn("Dropped message, USER_CHANGES Message has no changeset!");
|
||||||
return cb();
|
return cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
//get all Vars we need
|
//get all Vars we need
|
||||||
var baseRev = message.data.baseRev;
|
var baseRev = message.data.baseRev;
|
||||||
var wireApool = (new AttributePool()).fromJsonable(message.data.apool);
|
var wireApool = (new AttributePool()).fromJsonable(message.data.apool);
|
||||||
|
@ -596,12 +596,12 @@ function handleUserChanges(data, cb)
|
||||||
// The client might disconnect between our callbacks. We should still
|
// The client might disconnect between our callbacks. We should still
|
||||||
// finish processing the changeset, so keep a reference to the session.
|
// finish processing the changeset, so keep a reference to the session.
|
||||||
var thisSession = sessioninfos[client.id];
|
var thisSession = sessioninfos[client.id];
|
||||||
|
|
||||||
var r, apool, pad;
|
var r, apool, pad;
|
||||||
|
|
||||||
// Measure time to process edit
|
// Measure time to process edit
|
||||||
var stopWatch = stats.timer('edits').start();
|
var stopWatch = stats.timer('edits').start();
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the pad
|
//get the pad
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -617,7 +617,7 @@ function handleUserChanges(data, cb)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//ex. _checkChangesetAndPool
|
//ex. _checkChangesetAndPool
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Verify that the changeset has valid syntax and is in canonical form
|
// Verify that the changeset has valid syntax and is in canonical form
|
||||||
|
@ -644,9 +644,9 @@ function handleUserChanges(data, cb)
|
||||||
if('author' == attr[0] && attr[1] != thisSession.author) throw new Error("Trying to submit changes as another author in changeset "+changeset);
|
if('author' == attr[0] && attr[1] != thisSession.author) throw new Error("Trying to submit changes as another author in changeset "+changeset);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//ex. adoptChangesetAttribs
|
//ex. adoptChangesetAttribs
|
||||||
|
|
||||||
//Afaik, it copies the new attributes from the changeset, to the global Attribute Pool
|
//Afaik, it copies the new attributes from the changeset, to the global Attribute Pool
|
||||||
changeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool);
|
changeset = Changeset.moveOpsToNewPool(changeset, wireApool, pad.pool);
|
||||||
}
|
}
|
||||||
|
@ -657,7 +657,7 @@ function handleUserChanges(data, cb)
|
||||||
stats.meter('failedChangesets').mark();
|
stats.meter('failedChangesets').mark();
|
||||||
return callback(new Error("Can't apply USER_CHANGES, because "+e.message));
|
return callback(new Error("Can't apply USER_CHANGES, because "+e.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
//ex. applyUserChanges
|
//ex. applyUserChanges
|
||||||
apool = pad.pool;
|
apool = pad.pool;
|
||||||
r = baseRev;
|
r = baseRev;
|
||||||
|
@ -671,7 +671,7 @@ function handleUserChanges(data, cb)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
r++;
|
r++;
|
||||||
|
|
||||||
pad.getRevisionChangeset(r, function(err, c)
|
pad.getRevisionChangeset(r, function(err, c)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
@ -704,16 +704,16 @@ function handleUserChanges(data, cb)
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
var prevText = pad.text();
|
var prevText = pad.text();
|
||||||
|
|
||||||
if (Changeset.oldLen(changeset) != prevText.length)
|
if (Changeset.oldLen(changeset) != prevText.length)
|
||||||
{
|
{
|
||||||
client.json.send({disconnect:"badChangeset"});
|
client.json.send({disconnect:"badChangeset"});
|
||||||
stats.meter('failedChangesets').mark();
|
stats.meter('failedChangesets').mark();
|
||||||
return callback(new Error("Can't apply USER_CHANGES "+changeset+" with oldLen " + Changeset.oldLen(changeset) + " to document of length " + prevText.length));
|
return callback(new Error("Can't apply USER_CHANGES "+changeset+" with oldLen " + Changeset.oldLen(changeset) + " to document of length " + prevText.length));
|
||||||
}
|
}
|
||||||
|
|
||||||
pad.appendRevision(changeset, thisSession.author);
|
pad.appendRevision(changeset, thisSession.author);
|
||||||
|
|
||||||
var correctionChangeset = _correctMarkersInPad(pad.atext, pad.pool);
|
var correctionChangeset = _correctMarkersInPad(pad.atext, pad.pool);
|
||||||
if (correctionChangeset) {
|
if (correctionChangeset) {
|
||||||
pad.appendRevision(correctionChangeset);
|
pad.appendRevision(correctionChangeset);
|
||||||
|
@ -724,7 +724,7 @@ function handleUserChanges(data, cb)
|
||||||
var nlChangeset = Changeset.makeSplice(pad.text(), pad.text().length-1, 0, "\n");
|
var nlChangeset = Changeset.makeSplice(pad.text(), pad.text().length-1, 0, "\n");
|
||||||
pad.appendRevision(nlChangeset);
|
pad.appendRevision(nlChangeset);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updatePadClients(pad, function(er) {
|
exports.updatePadClients(pad, function(er) {
|
||||||
ERR(er)
|
ERR(er)
|
||||||
});
|
});
|
||||||
|
@ -739,16 +739,16 @@ function handleUserChanges(data, cb)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updatePadClients = function(pad, callback)
|
exports.updatePadClients = function(pad, callback)
|
||||||
{
|
{
|
||||||
//skip this step if noone is on this pad
|
//skip this step if noone is on this pad
|
||||||
var roomClients = socketio.sockets.clients(pad.id);
|
var roomClients = socketio.sockets.clients(pad.id);
|
||||||
if(roomClients.length==0)
|
if(roomClients.length==0)
|
||||||
return callback();
|
return callback();
|
||||||
|
|
||||||
// since all clients usually get the same set of changesets, store them in local cache
|
// since all clients usually get the same set of changesets, store them in local cache
|
||||||
// to remove unnecessary roundtrip to the datalayer
|
// to remove unnecessary roundtrip to the datalayer
|
||||||
// TODO: in REAL world, if we're working without datalayer cache, all requests to revisions will be fired
|
// TODO: in REAL world, if we're working without datalayer cache, all requests to revisions will be fired
|
||||||
// BEFORE first result will be landed to our cache object. The solution is to replace parallel processing
|
// BEFORE first result will be landed to our cache object. The solution is to replace parallel processing
|
||||||
// via async.forEach with sequential for() loop. There is no real benefits of running this in parallel,
|
// via async.forEach with sequential for() loop. There is no real benefits of running this in parallel,
|
||||||
// but benefit of reusing cached revision object is HUGE
|
// but benefit of reusing cached revision object is HUGE
|
||||||
var revCache = {};
|
var revCache = {};
|
||||||
|
@ -763,7 +763,7 @@ exports.updatePadClients = function(pad, callback)
|
||||||
async.whilst(
|
async.whilst(
|
||||||
function (){ return sessioninfos[sid] && sessioninfos[sid].rev < pad.getHeadRevisionNumber()},
|
function (){ return sessioninfos[sid] && sessioninfos[sid].rev < pad.getHeadRevisionNumber()},
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var r = sessioninfos[sid].rev + 1;
|
var r = sessioninfos[sid].rev + 1;
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
|
@ -772,7 +772,7 @@ exports.updatePadClients = function(pad, callback)
|
||||||
callback(null, revCache[r]);
|
callback(null, revCache[r]);
|
||||||
else
|
else
|
||||||
pad.getRevision(r, callback);
|
pad.getRevision(r, callback);
|
||||||
},
|
},
|
||||||
function(revision, callback)
|
function(revision, callback)
|
||||||
{
|
{
|
||||||
revCache[r] = revision;
|
revCache[r] = revision;
|
||||||
|
@ -800,8 +800,8 @@ exports.updatePadClients = function(pad, callback)
|
||||||
author: author,
|
author: author,
|
||||||
currentTime: currentTime,
|
currentTime: currentTime,
|
||||||
timeDelta: currentTime - sessioninfos[sid].time
|
timeDelta: currentTime - sessioninfos[sid].time
|
||||||
}};
|
}};
|
||||||
|
|
||||||
client.json.send(wireMsg);
|
client.json.send(wireMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -814,7 +814,7 @@ exports.updatePadClients = function(pad, callback)
|
||||||
},
|
},
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
},callback);
|
},callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -830,11 +830,11 @@ function _correctMarkersInPad(atext, apool) {
|
||||||
var offset = 0;
|
var offset = 0;
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
var op = iter.next();
|
var op = iter.next();
|
||||||
|
|
||||||
var hasMarker = _.find(AttributeManager.lineAttributes, function(attribute){
|
var hasMarker = _.find(AttributeManager.lineAttributes, function(attribute){
|
||||||
return Changeset.opAttributeValue(op, attribute, apool);
|
return Changeset.opAttributeValue(op, attribute, apool);
|
||||||
}) !== undefined;
|
}) !== undefined;
|
||||||
|
|
||||||
if (hasMarker) {
|
if (hasMarker) {
|
||||||
for(var i=0;i<op.chars;i++) {
|
for(var i=0;i<op.chars;i++) {
|
||||||
if (offset > 0 && text.charAt(offset-1) != '\n') {
|
if (offset > 0 && text.charAt(offset-1) != '\n') {
|
||||||
|
@ -864,7 +864,7 @@ function _correctMarkersInPad(atext, apool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a CLIENT_READY. A CLIENT_READY is the first message from the client to the server. The Client sends his token
|
* Handles a CLIENT_READY. A CLIENT_READY is the first message from the client to the server. The Client sends his token
|
||||||
* and the pad it wants to enter. The Server answers with the inital values (clientVars) of the pad
|
* and the pad it wants to enter. The Server answers with the inital values (clientVars) of the pad
|
||||||
* @param client the client that send this message
|
* @param client the client that send this message
|
||||||
* @param message the message from the client
|
* @param message the message from the client
|
||||||
|
@ -922,7 +922,7 @@ function handleClientReady(client, message)
|
||||||
securityManager.checkAccess (padIds.padId, message.sessionID, message.token, message.password, function(err, statusObject)
|
securityManager.checkAccess (padIds.padId, message.sessionID, message.token, message.password, function(err, statusObject)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//access was granted
|
//access was granted
|
||||||
if(statusObject.accessStatus == "grant")
|
if(statusObject.accessStatus == "grant")
|
||||||
{
|
{
|
||||||
|
@ -935,7 +935,7 @@ function handleClientReady(client, message)
|
||||||
client.json.send({accessStatus: statusObject.accessStatus})
|
client.json.send({accessStatus: statusObject.accessStatus})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
//get all authordata of this new user, and load the pad-object from the database
|
//get all authordata of this new user, and load the pad-object from the database
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
|
@ -967,7 +967,7 @@ function handleClientReady(client, message)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var authors = pad.getAllAuthors();
|
var authors = pad.getAllAuthors();
|
||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
//get timestamp of latest revission needed for timeslider
|
//get timestamp of latest revission needed for timeslider
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -993,7 +993,7 @@ function handleClientReady(client, message)
|
||||||
}, callback);
|
}, callback);
|
||||||
}
|
}
|
||||||
], callback);
|
], callback);
|
||||||
|
|
||||||
},
|
},
|
||||||
//glue the clientVars together, send them and tell the other clients that a new one is there
|
//glue the clientVars together, send them and tell the other clients that a new one is there
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -1013,12 +1013,12 @@ function handleClientReady(client, message)
|
||||||
roomClients[i].json.send({disconnect:"userdup"});
|
roomClients[i].json.send({disconnect:"userdup"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save in sessioninfos that this session belonges to this pad
|
//Save in sessioninfos that this session belonges to this pad
|
||||||
sessioninfos[client.id].padId = padIds.padId;
|
sessioninfos[client.id].padId = padIds.padId;
|
||||||
sessioninfos[client.id].readOnlyPadId = padIds.readOnlyPadId;
|
sessioninfos[client.id].readOnlyPadId = padIds.readOnlyPadId;
|
||||||
sessioninfos[client.id].readonly = padIds.readonly;
|
sessioninfos[client.id].readonly = padIds.readonly;
|
||||||
|
|
||||||
//Log creation/(re-)entering of a pad
|
//Log creation/(re-)entering of a pad
|
||||||
client.get('remoteAddress', function(er, ip) {
|
client.get('remoteAddress', function(er, ip) {
|
||||||
//Anonymize the IP address if IP logging is disabled
|
//Anonymize the IP address if IP logging is disabled
|
||||||
|
@ -1056,7 +1056,7 @@ function handleClientReady(client, message)
|
||||||
client.json.send({disconnect:"corruptPad"});// pull the breaks
|
client.json.send({disconnect:"corruptPad"});// pull the breaks
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warning: never ever send padIds.padId to the client. If the
|
// Warning: never ever send padIds.padId to the client. If the
|
||||||
// client is read only you would open a security hole 1 swedish
|
// client is read only you would open a security hole 1 swedish
|
||||||
// mile wide...
|
// mile wide...
|
||||||
|
@ -1085,7 +1085,7 @@ function handleClientReady(client, message)
|
||||||
"padId": message.padId,
|
"padId": message.padId,
|
||||||
"initialTitle": "Pad: " + message.padId,
|
"initialTitle": "Pad: " + message.padId,
|
||||||
"opts": {},
|
"opts": {},
|
||||||
// tell the client the number of the latest chat-message, which will be
|
// tell the client the number of the latest chat-message, which will be
|
||||||
// used to request the latest 100 chat-messages later (GET_CHAT_MESSAGES)
|
// used to request the latest 100 chat-messages later (GET_CHAT_MESSAGES)
|
||||||
"chatHead": pad.chatHead,
|
"chatHead": pad.chatHead,
|
||||||
"numConnectedUsers": roomClients.length,
|
"numConnectedUsers": roomClients.length,
|
||||||
|
@ -1093,7 +1093,7 @@ function handleClientReady(client, message)
|
||||||
"readonly": padIds.readonly,
|
"readonly": padIds.readonly,
|
||||||
"serverTimestamp": new Date().getTime(),
|
"serverTimestamp": new Date().getTime(),
|
||||||
"userId": author,
|
"userId": author,
|
||||||
"abiwordAvailable": settings.abiwordAvailable(),
|
"abiwordAvailable": settings.abiwordAvailable(),
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"plugins": plugins.plugins,
|
"plugins": plugins.plugins,
|
||||||
"parts": plugins.parts,
|
"parts": plugins.parts,
|
||||||
|
@ -1106,18 +1106,18 @@ function handleClientReady(client, message)
|
||||||
{
|
{
|
||||||
clientVars.userName = authorName;
|
clientVars.userName = authorName;
|
||||||
}
|
}
|
||||||
|
|
||||||
//call the clientVars-hook so plugins can modify them before they get sent to the client
|
//call the clientVars-hook so plugins can modify them before they get sent to the client
|
||||||
hooks.aCallAll("clientVars", { clientVars: clientVars, pad: pad }, function ( err, messages ) {
|
hooks.aCallAll("clientVars", { clientVars: clientVars, pad: pad }, function ( err, messages ) {
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
_.each(messages, function(newVars) {
|
_.each(messages, function(newVars) {
|
||||||
//combine our old object with the new attributes from the hook
|
//combine our old object with the new attributes from the hook
|
||||||
for(var attr in newVars) {
|
for(var attr in newVars) {
|
||||||
clientVars[attr] = newVars[attr];
|
clientVars[attr] = newVars[attr];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Join the pad and start receiving updates
|
//Join the pad and start receiving updates
|
||||||
client.join(padIds.padId);
|
client.join(padIds.padId);
|
||||||
//Send the clientVars to the Client
|
//Send the clientVars to the Client
|
||||||
|
@ -1126,9 +1126,9 @@ function handleClientReady(client, message)
|
||||||
sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
|
sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sessioninfos[client.id].author = author;
|
sessioninfos[client.id].author = author;
|
||||||
|
|
||||||
//prepare the notification for the other users on the pad, that this user joined
|
//prepare the notification for the other users on the pad, that this user joined
|
||||||
var messageToTheOtherUsers = {
|
var messageToTheOtherUsers = {
|
||||||
"type": "COLLABROOM",
|
"type": "COLLABROOM",
|
||||||
|
@ -1142,7 +1142,7 @@ function handleClientReady(client, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//Add the authorname of this new User, if avaiable
|
//Add the authorname of this new User, if avaiable
|
||||||
if(authorName != null)
|
if(authorName != null)
|
||||||
{
|
{
|
||||||
|
@ -1151,7 +1151,7 @@ function handleClientReady(client, message)
|
||||||
|
|
||||||
// notify all existing users about new user
|
// notify all existing users about new user
|
||||||
client.broadcast.to(padIds.padId).json.send(messageToTheOtherUsers);
|
client.broadcast.to(padIds.padId).json.send(messageToTheOtherUsers);
|
||||||
|
|
||||||
//Run trough all sessions of this pad
|
//Run trough all sessions of this pad
|
||||||
async.forEach(socketio.sockets.clients(padIds.padId), function(roomClient, callback)
|
async.forEach(socketio.sockets.clients(padIds.padId), function(roomClient, callback)
|
||||||
{
|
{
|
||||||
|
@ -1160,9 +1160,9 @@ function handleClientReady(client, message)
|
||||||
//Jump over, if this session is the connection session
|
//Jump over, if this session is the connection session
|
||||||
if(roomClient.id == client.id)
|
if(roomClient.id == client.id)
|
||||||
return callback();
|
return callback();
|
||||||
|
|
||||||
|
|
||||||
//Since sessioninfos might change while being enumerated, check if the
|
//Since sessioninfos might change while being enumerated, check if the
|
||||||
//sessionID is still assigned to a valid session
|
//sessionID is still assigned to a valid session
|
||||||
if(sessioninfos[roomClient.id] !== undefined)
|
if(sessioninfos[roomClient.id] !== undefined)
|
||||||
author = sessioninfos[roomClient.id].author;
|
author = sessioninfos[roomClient.id].author;
|
||||||
|
@ -1178,7 +1178,7 @@ function handleClientReady(client, message)
|
||||||
callback(null, historicalAuthorData[author]);
|
callback(null, historicalAuthorData[author]);
|
||||||
else
|
else
|
||||||
authorManager.getAuthor(author, callback);
|
authorManager.getAuthor(author, callback);
|
||||||
},
|
},
|
||||||
function (authorInfo, callback)
|
function (authorInfo, callback)
|
||||||
{
|
{
|
||||||
//Send the new User a Notification about this other user
|
//Send the new User a Notification about this other user
|
||||||
|
@ -1207,7 +1207,7 @@ function handleClientReady(client, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a request for a rough changeset, the timeslider client needs it
|
* Handles a request for a rough changeset, the timeslider client needs it
|
||||||
*/
|
*/
|
||||||
function handleChangesetRequest(client, message)
|
function handleChangesetRequest(client, message)
|
||||||
{
|
{
|
||||||
|
@ -1237,7 +1237,7 @@ function handleChangesetRequest(client, message)
|
||||||
messageLogger.warn("Dropped message, changeset request has no requestID!");
|
messageLogger.warn("Dropped message, changeset request has no requestID!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var granularity = message.data.granularity;
|
var granularity = message.data.granularity;
|
||||||
var start = message.data.start;
|
var start = message.data.start;
|
||||||
var end = start + (100 * granularity);
|
var end = start + (100 * granularity);
|
||||||
|
@ -1281,47 +1281,49 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
|
||||||
var composedChangesets = {};
|
var composedChangesets = {};
|
||||||
var revisionDate = [];
|
var revisionDate = [];
|
||||||
var lines;
|
var lines;
|
||||||
|
var head_revision = 0;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the pad from the database
|
//get the pad from the database
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(padId, function(err, _pad)
|
padManager.getPad(padId, function(err, _pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
pad = _pad;
|
pad = _pad;
|
||||||
|
head_revision = pad.getHeadRevisionNumber();
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//calculate the last full endnum
|
//calculate the last full endnum
|
||||||
var lastRev = pad.getHeadRevisionNumber();
|
var lastRev = pad.getHeadRevisionNumber();
|
||||||
if (endNum > lastRev+1) {
|
if (endNum > lastRev+1) {
|
||||||
endNum = lastRev+1;
|
endNum = lastRev+1;
|
||||||
}
|
}
|
||||||
endNum = Math.floor(endNum / granularity)*granularity;
|
endNum = Math.floor(endNum / granularity)*granularity;
|
||||||
|
|
||||||
var compositesChangesetNeeded = [];
|
var compositesChangesetNeeded = [];
|
||||||
var revTimesNeeded = [];
|
var revTimesNeeded = [];
|
||||||
|
|
||||||
//figure out which composite Changeset and revTimes we need, to load them in bulk
|
//figure out which composite Changeset and revTimes we need, to load them in bulk
|
||||||
var compositeStart = startNum;
|
var compositeStart = startNum;
|
||||||
while (compositeStart < endNum)
|
while (compositeStart < endNum)
|
||||||
{
|
{
|
||||||
var compositeEnd = compositeStart + granularity;
|
var compositeEnd = compositeStart + granularity;
|
||||||
|
|
||||||
//add the composite Changeset we needed
|
//add the composite Changeset we needed
|
||||||
compositesChangesetNeeded.push({start: compositeStart, end: compositeEnd});
|
compositesChangesetNeeded.push({start: compositeStart, end: compositeEnd});
|
||||||
|
|
||||||
//add the t1 time we need
|
//add the t1 time we need
|
||||||
revTimesNeeded.push(compositeStart == 0 ? 0 : compositeStart - 1);
|
revTimesNeeded.push(compositeStart == 0 ? 0 : compositeStart - 1);
|
||||||
//add the t2 time we need
|
//add the t2 time we need
|
||||||
revTimesNeeded.push(compositeEnd - 1);
|
revTimesNeeded.push(compositeEnd - 1);
|
||||||
|
|
||||||
compositeStart += granularity;
|
compositeStart += granularity;
|
||||||
}
|
}
|
||||||
|
|
||||||
//get all needed db values parallel
|
//get all needed db values parallel
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(callback)
|
function(callback)
|
||||||
|
@ -1358,58 +1360,57 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
lines = _lines;
|
lines = _lines;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
], callback);
|
], callback);
|
||||||
},
|
},
|
||||||
//doesn't know what happens here excatly :/
|
//doesn't know what happens here excatly :/
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var compositeStart = startNum;
|
var compositeStart = startNum;
|
||||||
|
|
||||||
while (compositeStart < endNum)
|
while (compositeStart < endNum)
|
||||||
{
|
{
|
||||||
if (compositeStart + granularity > endNum)
|
var compositeEnd = compositeStart + granularity;
|
||||||
|
if (compositeEnd > endNum || compositeEnd > head_revision)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
var compositeEnd = compositeStart + granularity;
|
|
||||||
|
|
||||||
var forwards = composedChangesets[compositeStart + "/" + compositeEnd];
|
var forwards = composedChangesets[compositeStart + "/" + compositeEnd];
|
||||||
var backwards = Changeset.inverse(forwards, lines.textlines, lines.alines, pad.apool());
|
var backwards = Changeset.inverse(forwards, lines.textlines, lines.alines, pad.apool());
|
||||||
|
|
||||||
Changeset.mutateAttributionLines(forwards, lines.alines, pad.apool());
|
Changeset.mutateAttributionLines(forwards, lines.alines, pad.apool());
|
||||||
Changeset.mutateTextLines(forwards, lines.textlines);
|
Changeset.mutateTextLines(forwards, lines.textlines);
|
||||||
|
|
||||||
var forwards2 = Changeset.moveOpsToNewPool(forwards, pad.apool(), apool);
|
var forwards2 = Changeset.moveOpsToNewPool(forwards, pad.apool(), apool);
|
||||||
var backwards2 = Changeset.moveOpsToNewPool(backwards, pad.apool(), apool);
|
var backwards2 = Changeset.moveOpsToNewPool(backwards, pad.apool(), apool);
|
||||||
|
|
||||||
var t1, t2;
|
var t1, t2;
|
||||||
if (compositeStart == 0)
|
if (compositeStart == 0)
|
||||||
{
|
{
|
||||||
t1 = revisionDate[0];
|
t1 = revisionDate[0];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
t1 = revisionDate[compositeStart - 1];
|
t1 = revisionDate[compositeStart - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
t2 = revisionDate[compositeEnd - 1];
|
t2 = revisionDate[compositeEnd - 1];
|
||||||
|
|
||||||
timeDeltas.push(t2 - t1);
|
timeDeltas.push(t2 - t1);
|
||||||
forwardsChangesets.push(forwards2);
|
forwardsChangesets.push(forwards2);
|
||||||
backwardsChangesets.push(backwards2);
|
backwardsChangesets.push(backwards2);
|
||||||
|
|
||||||
compositeStart += granularity;
|
compositeStart += granularity;
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
callback(null, {forwardsChangesets: forwardsChangesets,
|
callback(null, {forwardsChangesets: forwardsChangesets,
|
||||||
backwardsChangesets: backwardsChangesets,
|
backwardsChangesets: backwardsChangesets,
|
||||||
apool: apool.toJsonable(),
|
apool: apool.toJsonable(),
|
||||||
|
@ -1424,7 +1425,7 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
|
||||||
* Tries to rebuild the getPadLines function of the original Etherpad
|
* Tries to rebuild the getPadLines function of the original Etherpad
|
||||||
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L263
|
* https://github.com/ether/pad/blob/master/etherpad/src/etherpad/control/pad/pad_changeset_control.js#L263
|
||||||
*/
|
*/
|
||||||
function getPadLines(padId, revNum, callback)
|
function getPadLines(padId, revNum, callback)
|
||||||
{
|
{
|
||||||
var atext;
|
var atext;
|
||||||
var result = {};
|
var result = {};
|
||||||
|
@ -1435,7 +1436,7 @@ function getPadLines(padId, revNum, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(padId, function(err, _pad)
|
padManager.getPad(padId, function(err, _pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
pad = _pad;
|
pad = _pad;
|
||||||
callback();
|
callback();
|
||||||
|
@ -1479,7 +1480,7 @@ function getPadLines(padId, revNum, callback)
|
||||||
function composePadChangesets(padId, startNum, endNum, callback)
|
function composePadChangesets(padId, startNum, endNum, callback)
|
||||||
{
|
{
|
||||||
var pad;
|
var pad;
|
||||||
var changesets = [];
|
var changesets = {};
|
||||||
var changeset;
|
var changeset;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
|
@ -1497,14 +1498,19 @@ function composePadChangesets(padId, startNum, endNum, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var changesetsNeeded=[];
|
var changesetsNeeded=[];
|
||||||
|
|
||||||
//create a array for all changesets, we will
|
var headNum = pad.getHeadRevisionNumber();
|
||||||
|
if (endNum > headNum)
|
||||||
|
endNum = headNum;
|
||||||
|
if (startNum < 0)
|
||||||
|
startNum = 0;
|
||||||
|
//create a array for all changesets, we will
|
||||||
//replace the values with the changeset later
|
//replace the values with the changeset later
|
||||||
for(var r=startNum;r<endNum;r++)
|
for(var r=startNum;r<endNum;r++)
|
||||||
{
|
{
|
||||||
changesetsNeeded.push(r);
|
changesetsNeeded.push(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
//get all changesets
|
//get all changesets
|
||||||
async.forEach(changesetsNeeded, function(revNum,callback)
|
async.forEach(changesetsNeeded, function(revNum,callback)
|
||||||
{
|
{
|
||||||
|
@ -1521,13 +1527,13 @@ function composePadChangesets(padId, startNum, endNum, callback)
|
||||||
{
|
{
|
||||||
changeset = changesets[startNum];
|
changeset = changesets[startNum];
|
||||||
var pool = pad.apool();
|
var pool = pad.apool();
|
||||||
|
|
||||||
for(var r=startNum+1;r<endNum;r++)
|
for(var r=startNum+1;r<endNum;r++)
|
||||||
{
|
{
|
||||||
var cs = changesets[r];
|
var cs = changesets[r];
|
||||||
changeset = Changeset.compose(changeset, cs, pool);
|
changeset = Changeset.compose(changeset, cs, pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null);
|
callback(null);
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue