mirror of
https://github.com/ether/etherpad-lite.git
synced 2025-05-05 06:37:10 -04:00
Merge c19444f6c9
into 8e6b451459
This commit is contained in:
commit
7cde49f981
19 changed files with 1102 additions and 964 deletions
|
@ -19,58 +19,64 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var db = require("./DB").db;
|
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
|
|
||||||
var randomString = require("../utils/randomstring");
|
var randomString = require("../utils/randomstring");
|
||||||
|
|
||||||
|
var AuthorManager = function AuthorManager(db) {
|
||||||
|
this.db = db;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.AuthorManager = AuthorManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the author exists
|
* Checks if the author exists
|
||||||
*/
|
*/
|
||||||
exports.doesAuthorExists = function (authorID, callback)
|
AuthorManager.prototype.doesAuthorExists = function (authorID, callback)
|
||||||
{
|
{
|
||||||
//check if the database entry of this author exists
|
//check if the database entry of this author exists
|
||||||
db.get("globalAuthor:" + authorID, function (err, author)
|
this.db.get("globalAuthor:" + authorID, function (err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, author != null);
|
callback(null, author != null);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the AuthorID for a token.
|
* Returns the AuthorID for a token.
|
||||||
* @param {String} token The token
|
* @param {String} token The token
|
||||||
* @param {Function} callback callback (err, author)
|
* @param {Function} callback callback (err, author)
|
||||||
*/
|
*/
|
||||||
exports.getAuthor4Token = function (token, callback)
|
AuthorManager.prototype.getAuthor4Token = function (token, callback)
|
||||||
{
|
{
|
||||||
mapAuthorWithDBKey("token2author", token, function(err, author)
|
this.mapAuthorWithDBKey("token2author", token, function(err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
//return only the sub value authorID
|
//return only the sub value authorID
|
||||||
callback(null, author ? author.authorID : author);
|
callback(null, author ? author.authorID : author);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the AuthorID for a mapper.
|
* Returns the AuthorID for a mapper.
|
||||||
* @param {String} token The mapper
|
* @param {String} token The mapper
|
||||||
* @param {Function} callback callback (err, author)
|
* @param {Function} callback callback (err, author)
|
||||||
*/
|
*/
|
||||||
exports.createAuthorIfNotExistsFor = function (authorMapper, name, callback)
|
AuthorManager.prototype.createAuthorIfNotExistsFor = function (authorMapper, name, callback)
|
||||||
{
|
{
|
||||||
mapAuthorWithDBKey("mapper2author", authorMapper, function(err, author)
|
var that = this;
|
||||||
|
this.mapAuthorWithDBKey("mapper2author", authorMapper, function(err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//set the name of this author
|
//set the name of this author
|
||||||
if(name)
|
if(name)
|
||||||
exports.setAuthorName(author.authorID, name);
|
that.setAuthorName(author.authorID, name);
|
||||||
|
|
||||||
//return the authorID
|
//return the authorID
|
||||||
callback(null, author);
|
callback(null, author);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the AuthorID for a mapper. We can map using a mapperkey,
|
* Returns the AuthorID for a mapper. We can map using a mapperkey,
|
||||||
|
@ -79,44 +85,42 @@ exports.createAuthorIfNotExistsFor = function (authorMapper, name, callback)
|
||||||
* @param {String} mapper The mapper
|
* @param {String} mapper The mapper
|
||||||
* @param {Function} callback callback (err, author)
|
* @param {Function} callback callback (err, author)
|
||||||
*/
|
*/
|
||||||
function mapAuthorWithDBKey (mapperkey, mapper, callback)
|
AuthorManager.prototype.mapAuthorWithDBKey = function mapAuthorWithDBKey (mapperkey, mapper, callback) {
|
||||||
{
|
var that = this;
|
||||||
//try to map to an author
|
//try to map to an author
|
||||||
db.get(mapperkey + ":" + mapper, function (err, author)
|
this.db.get(mapperkey + ":" + mapper, function (err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//there is no author with this mapper, so create one
|
//there is no author with this mapper, so create one
|
||||||
if(author == null)
|
if(author == null)
|
||||||
{
|
{
|
||||||
exports.createAuthor(null, function(err, author)
|
that.createAuthor(null, function(err, author) {
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//create the token2author relation
|
//create the token2author relation
|
||||||
db.set(mapperkey + ":" + mapper, author.authorID);
|
that.db.set(mapperkey + ":" + mapper, author.authorID);
|
||||||
|
|
||||||
//return the author
|
//return the author
|
||||||
callback(null, author);
|
callback(null, author);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
//there is a author with this mapper
|
//there is a author with this mapper
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
//update the timestamp of this author
|
//update the timestamp of this author
|
||||||
db.setSub("globalAuthor:" + author, ["timestamp"], new Date().getTime());
|
that.db.setSub("globalAuthor:" + author, ["timestamp"], new Date().getTime());
|
||||||
|
|
||||||
//return the author
|
//return the author
|
||||||
callback(null, {authorID: author});
|
callback(null, {authorID: author});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal function that creates the database entry for an author
|
* Internal function that creates the database entry for an author
|
||||||
* @param {String} name The name of the author
|
* @param {String} name The name of the author
|
||||||
*/
|
*/
|
||||||
exports.createAuthor = function(name, callback)
|
AuthorManager.prototype.createAuthor = function(name, callback)
|
||||||
{
|
{
|
||||||
//create the new author name
|
//create the new author name
|
||||||
var author = "a." + randomString(16);
|
var author = "a." + randomString(16);
|
||||||
|
@ -125,57 +129,57 @@ exports.createAuthor = function(name, callback)
|
||||||
var authorObj = {"colorId" : Math.floor(Math.random()*32), "name": name, "timestamp": new Date().getTime()};
|
var authorObj = {"colorId" : Math.floor(Math.random()*32), "name": name, "timestamp": new Date().getTime()};
|
||||||
|
|
||||||
//set the global author db entry
|
//set the global author db entry
|
||||||
db.set("globalAuthor:" + author, authorObj);
|
this.db.set("globalAuthor:" + author, authorObj);
|
||||||
|
|
||||||
callback(null, {authorID: author});
|
callback(null, {authorID: author});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Author Obj of the author
|
* Returns the Author Obj of the author
|
||||||
* @param {String} author The id of the author
|
* @param {String} author The id of the author
|
||||||
* @param {Function} callback callback(err, authorObj)
|
* @param {Function} callback callback(err, authorObj)
|
||||||
*/
|
*/
|
||||||
exports.getAuthor = function (author, callback)
|
AuthorManager.prototype.getAuthor = function (author, callback)
|
||||||
{
|
{
|
||||||
db.get("globalAuthor:" + author, callback);
|
this.db.get("globalAuthor:" + author, callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the color Id of the author
|
* Returns the color Id of the author
|
||||||
* @param {String} author The id of the author
|
* @param {String} author The id of the author
|
||||||
* @param {Function} callback callback(err, colorId)
|
* @param {Function} callback callback(err, colorId)
|
||||||
*/
|
*/
|
||||||
exports.getAuthorColorId = function (author, callback)
|
AuthorManager.prototype.getAuthorColorId = function (author, callback)
|
||||||
{
|
{
|
||||||
db.getSub("globalAuthor:" + author, ["colorId"], callback);
|
this.db.getSub("globalAuthor:" + author, ["colorId"], callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the color Id of the author
|
* Sets the color Id of the author
|
||||||
* @param {String} author The id of the author
|
* @param {String} author The id of the author
|
||||||
* @param {Function} callback (optional)
|
* @param {Function} callback (optional)
|
||||||
*/
|
*/
|
||||||
exports.setAuthorColorId = function (author, colorId, callback)
|
AuthorManager.prototype.setAuthorColorId = function (author, colorId, callback)
|
||||||
{
|
{
|
||||||
db.setSub("globalAuthor:" + author, ["colorId"], colorId, callback);
|
this.db.setSub("globalAuthor:" + author, ["colorId"], colorId, callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the name of the author
|
* Returns the name of the author
|
||||||
* @param {String} author The id of the author
|
* @param {String} author The id of the author
|
||||||
* @param {Function} callback callback(err, name)
|
* @param {Function} callback callback(err, name)
|
||||||
*/
|
*/
|
||||||
exports.getAuthorName = function (author, callback)
|
AuthorManager.prototype.getAuthorName = function (author, callback)
|
||||||
{
|
{
|
||||||
db.getSub("globalAuthor:" + author, ["name"], callback);
|
this.db.getSub("globalAuthor:" + author, ["name"], callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the name of the author
|
* Sets the name of the author
|
||||||
* @param {String} author The id of the author
|
* @param {String} author The id of the author
|
||||||
* @param {Function} callback (optional)
|
* @param {Function} callback (optional)
|
||||||
*/
|
*/
|
||||||
exports.setAuthorName = function (author, name, callback)
|
AuthorManager.prototype.setAuthorName = function (author, name, callback)
|
||||||
{
|
{
|
||||||
db.setSub("globalAuthor:" + author, ["name"], name, callback);
|
this.db.setSub("globalAuthor:" + author, ["name"], name, callback);
|
||||||
}
|
};
|
||||||
|
|
|
@ -20,38 +20,25 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ueberDB = require("ueberDB");
|
var ueberDB = require("ueberDB");
|
||||||
var settings = require("../utils/Settings");
|
|
||||||
var log4js = require('log4js');
|
var log4js = require('log4js');
|
||||||
|
|
||||||
//set database settings
|
|
||||||
var db = new ueberDB.database(settings.dbType, settings.dbSettings, null, log4js.getLogger("ueberDB"));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The UeberDB Object that provides the database functions
|
|
||||||
*/
|
|
||||||
exports.db = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initalizes the database with the settings provided by the settings module
|
* Initalizes the database with the settings provided by the settings module
|
||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
exports.init = function(callback)
|
exports.init = function init(settings, callback) {
|
||||||
{
|
//set database settings
|
||||||
|
console.log(settings);
|
||||||
|
var db = new ueberDB.database(settings.dbType, settings.dbSettings, null, log4js.getLogger("ueberDB"));
|
||||||
|
|
||||||
//initalize the database async
|
//initalize the database async
|
||||||
db.init(function(err)
|
db.init(function(error){
|
||||||
{
|
|
||||||
//there was an error while initializing the database, output it and stop
|
//there was an error while initializing the database, output it and stop
|
||||||
if(err)
|
if(error) {
|
||||||
{
|
callback(error, null);
|
||||||
console.error("ERROR: Problem while initalizing the database");
|
|
||||||
console.error(err.stack ? err.stack : err);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
}
|
||||||
//everything ok
|
else {
|
||||||
else
|
callback(null, db);
|
||||||
{
|
|
||||||
exports.db = db;
|
|
||||||
callback(null);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
|
@ -21,21 +21,28 @@
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var customError = require("../utils/customError");
|
var customError = require("../utils/customError");
|
||||||
var randomString = require("../utils/randomstring");
|
var randomString = require("../utils/randomstring");
|
||||||
var db = require("./DB").db;
|
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var padManager = require("./PadManager");
|
|
||||||
var sessionManager = require("./SessionManager");
|
|
||||||
|
|
||||||
exports.deleteGroup = function(groupID, callback)
|
var GroupManager = function GroupManager(db, padManager, SessionManager) {
|
||||||
|
this.db = db;
|
||||||
|
this.padManager = padManager;
|
||||||
|
this.SessionManager = SessionManager;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.GroupManager = GroupManager;
|
||||||
|
|
||||||
|
GroupManager.prototype.deleteGroup = function(groupID, callback)
|
||||||
{
|
{
|
||||||
var group;
|
var group;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//ensure group exists
|
//ensure group exists
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
//try to get the group entry
|
//try to get the group entry
|
||||||
db.get("group:" + groupID, function (err, _group)
|
that.db.get("group:" + groupID, function (err, _group)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -65,7 +72,7 @@ exports.deleteGroup = function(groupID, callback)
|
||||||
//loop trough all pads and delete them
|
//loop trough all pads and delete them
|
||||||
async.forEach(padIDs, function(padID, callback)
|
async.forEach(padIDs, function(padID, callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(padID, function(err, pad)
|
that.padManager.getPad(padID, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -77,7 +84,7 @@ exports.deleteGroup = function(groupID, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//try to get the group entry
|
//try to get the group entry
|
||||||
db.get("group2sessions:" + groupID, function (err, group2sessions)
|
that.db.get("group2sessions:" + groupID, function (err, group2sessions)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -101,8 +108,8 @@ exports.deleteGroup = function(groupID, callback)
|
||||||
//remove group and group2sessions entry
|
//remove group and group2sessions entry
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
db.remove("group2sessions:" + groupID);
|
that.db.remove("group2sessions:" + groupID);
|
||||||
db.remove("group:" + groupID);
|
that.db.remove("group:" + groupID);
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
|
@ -110,30 +117,31 @@ exports.deleteGroup = function(groupID, callback)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.doesGroupExist = function(groupID, callback)
|
GroupManager.prototype.doesGroupExist = function(groupID, callback)
|
||||||
{
|
{
|
||||||
//try to get the group entry
|
//try to get the group entry
|
||||||
db.get("group:" + groupID, function (err, group)
|
this.db.get("group:" + groupID, function (err, group)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, group != null);
|
callback(null, group != null);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.createGroup = function(callback)
|
GroupManager.prototype.createGroup = function(callback)
|
||||||
{
|
{
|
||||||
//search for non existing groupID
|
//search for non existing groupID
|
||||||
var groupID = "g." + randomString(16);
|
var groupID = "g." + randomString(16);
|
||||||
|
|
||||||
//create the group
|
//create the group
|
||||||
db.set("group:" + groupID, {pads: {}});
|
this.db.set("group:" + groupID, {pads: {}});
|
||||||
callback(null, {groupID: groupID});
|
callback(null, {groupID: groupID});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.createGroupIfNotExistsFor = function(groupMapper, callback)
|
GroupManager.prototype.createGroupIfNotExistsFor = function(groupMapper, callback)
|
||||||
{
|
{
|
||||||
|
var that = this;
|
||||||
//ensure mapper is optional
|
//ensure mapper is optional
|
||||||
if(typeof groupMapper != "string")
|
if(typeof groupMapper != "string")
|
||||||
{
|
{
|
||||||
|
@ -142,19 +150,19 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
//try to get a group for this mapper
|
//try to get a group for this mapper
|
||||||
db.get("mapper2group:"+groupMapper, function(err, groupID)
|
this.db.get("mapper2group:"+groupMapper, function(err, groupID)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//there is no group for this mapper, let's create a group
|
//there is no group for this mapper, let's create a group
|
||||||
if(groupID == null)
|
if(groupID == null)
|
||||||
{
|
{
|
||||||
exports.createGroup(function(err, responseObj)
|
that.createGroup(function(err, responseObj)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//create the mapper entry for this group
|
//create the mapper entry for this group
|
||||||
db.set("mapper2group:"+groupMapper, responseObj.groupID);
|
that.db.set("mapper2group:"+groupMapper, responseObj.groupID);
|
||||||
|
|
||||||
callback(null, responseObj);
|
callback(null, responseObj);
|
||||||
});
|
});
|
||||||
|
@ -166,10 +174,11 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback)
|
||||||
callback(null, {groupID: groupID});
|
callback(null, {groupID: groupID});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.createGroupPad = function(groupID, padName, text, callback)
|
GroupManager.prototype.createGroupPad = function(groupID, padName, text, callback)
|
||||||
{
|
{
|
||||||
|
var that = this;
|
||||||
//create the padID
|
//create the padID
|
||||||
var padID = groupID + "$" + padName;
|
var padID = groupID + "$" + padName;
|
||||||
|
|
||||||
|
@ -177,7 +186,7 @@ exports.createGroupPad = function(groupID, padName, text, callback)
|
||||||
//ensure group exists
|
//ensure group exists
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
exports.doesGroupExist(groupID, function(err, exists)
|
that.doesGroupExist(groupID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -196,12 +205,12 @@ exports.createGroupPad = function(groupID, padName, text, callback)
|
||||||
//ensure pad does not exists
|
//ensure pad does not exists
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
padManager.doesPadExists(padID, function(err, exists)
|
that.padManager.doesPadExists(padID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//pad exists already
|
//pad exists already
|
||||||
if(exists == true)
|
if(exists)
|
||||||
{
|
{
|
||||||
callback(new customError("padName does already exist","apierror"));
|
callback(new customError("padName does already exist","apierror"));
|
||||||
}
|
}
|
||||||
|
@ -215,7 +224,7 @@ exports.createGroupPad = function(groupID, padName, text, callback)
|
||||||
//create the pad
|
//create the pad
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(padID, text, function(err)
|
that.padManager.getPad(padID, text, function(err)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback();
|
callback();
|
||||||
|
@ -224,7 +233,7 @@ exports.createGroupPad = function(groupID, padName, text, callback)
|
||||||
//create an entry in the group for this pad
|
//create an entry in the group for this pad
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
db.setSub("group:" + groupID, ["pads", padID], 1);
|
that.db.setSub("group:" + groupID, ["pads", padID], 1);
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
|
@ -232,27 +241,28 @@ exports.createGroupPad = function(groupID, padName, text, callback)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, {padID: padID});
|
callback(null, {padID: padID});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.listPads = function(groupID, callback)
|
GroupManager.prototype.listPads = function(groupID, callback)
|
||||||
{
|
{
|
||||||
exports.doesGroupExist(groupID, function(err, exists)
|
var that = this;
|
||||||
|
this.doesGroupExist(groupID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//group does not exist
|
//group does not exist
|
||||||
if(exists == false)
|
if(!exists)
|
||||||
{
|
{
|
||||||
callback(new customError("groupID does not exist","apierror"));
|
callback(new customError("groupID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
//group exists, let's get the pads
|
//group exists, let's get the pads
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
db.getSub("group:" + groupID, ["pads"], function(err, pads)
|
that.db.getSub("group:" + groupID, ["pads"], function(err, pads)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, {padIDs: pads});
|
callback(null, {padIDs: pads});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
102
node/db/Pad.js
102
node/db/Pad.js
|
@ -1,17 +1,11 @@
|
||||||
/**
|
/**
|
||||||
* The pad object, defined with joose
|
* The pad object
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var Changeset = require("../utils/Changeset");
|
var Changeset = require("../utils/Changeset");
|
||||||
var AttributePoolFactory = require("../utils/AttributePoolFactory");
|
var AttributePoolFactory = require("../utils/AttributePoolFactory");
|
||||||
var db = require("./DB").db;
|
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var settings = require('../utils/Settings');
|
|
||||||
var authorManager = require("./AuthorManager");
|
|
||||||
var padManager = require("./PadManager");
|
|
||||||
var padMessageHandler = require("../handler/PadMessageHandler");
|
|
||||||
var readOnlyManager = require("./ReadOnlyManager");
|
|
||||||
var crypto = require("crypto");
|
var crypto = require("crypto");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,8 +67,8 @@ Pad.prototype.appendRevision = function appendRevision(aChangeset, author) {
|
||||||
newRevData.meta.atext = this.atext;
|
newRevData.meta.atext = this.atext;
|
||||||
}
|
}
|
||||||
|
|
||||||
db.set("pad:"+this.id+":revs:"+newRev, newRevData);
|
this.db.set("pad:"+this.id+":revs:"+newRev, newRevData);
|
||||||
db.set("pad:"+this.id, {atext: this.atext,
|
this.db.set("pad:"+this.id, {atext: this.atext,
|
||||||
pool: this.pool.toJsonable(),
|
pool: this.pool.toJsonable(),
|
||||||
head: this.head,
|
head: this.head,
|
||||||
chatHead: this.chatHead,
|
chatHead: this.chatHead,
|
||||||
|
@ -83,15 +77,15 @@ Pad.prototype.appendRevision = function appendRevision(aChangeset, author) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.getRevisionChangeset = function getRevisionChangeset(revNum, callback) {
|
Pad.prototype.getRevisionChangeset = function getRevisionChangeset(revNum, callback) {
|
||||||
db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
|
this.db.getSub("pad:"+this.id+":revs:"+revNum, ["changeset"], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.getRevisionAuthor = function getRevisionAuthor(revNum, callback) {
|
Pad.prototype.getRevisionAuthor = function getRevisionAuthor(revNum, callback) {
|
||||||
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "author"], callback);
|
this.db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "author"], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.getRevisionDate = function getRevisionDate(revNum, callback) {
|
Pad.prototype.getRevisionDate = function getRevisionDate(revNum, callback) {
|
||||||
db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "timestamp"], callback);
|
this.db.getSub("pad:"+this.id+":revs:"+revNum, ["meta", "timestamp"], callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.getAllAuthors = function getAllAuthors() {
|
Pad.prototype.getAllAuthors = function getAllAuthors() {
|
||||||
|
@ -109,7 +103,7 @@ Pad.prototype.getAllAuthors = function getAllAuthors() {
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targetRev, callback) {
|
Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targetRev, callback) {
|
||||||
var _this = this;
|
var that = this;
|
||||||
|
|
||||||
var keyRev = this.getKeyRevisionNumber(targetRev);
|
var keyRev = this.getKeyRevisionNumber(targetRev);
|
||||||
var atext;
|
var atext;
|
||||||
|
@ -132,7 +126,7 @@ Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targe
|
||||||
//get the atext of the key revision
|
//get the atext of the key revision
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
db.getSub("pad:"+_this.id+":revs:"+keyRev, ["meta", "atext"], function(err, _atext)
|
that.db.getSub("pad:"+that.id+":revs:"+keyRev, ["meta", "atext"], function(err, _atext)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
atext = Changeset.cloneAText(_atext);
|
atext = Changeset.cloneAText(_atext);
|
||||||
|
@ -144,7 +138,7 @@ Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targe
|
||||||
{
|
{
|
||||||
async.forEach(neededChangesets, function(item, callback)
|
async.forEach(neededChangesets, function(item, callback)
|
||||||
{
|
{
|
||||||
_this.getRevisionChangeset(item, function(err, changeset)
|
that.getRevisionChangeset(item, function(err, changeset)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
changesets[item] = changeset;
|
changesets[item] = changeset;
|
||||||
|
@ -157,7 +151,7 @@ Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targe
|
||||||
//apply all changesets to the key changeset
|
//apply all changesets to the key changeset
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var apool = _this.apool();
|
var apool = that.apool();
|
||||||
var curRev = keyRev;
|
var curRev = keyRev;
|
||||||
|
|
||||||
while (curRev < targetRev)
|
while (curRev < targetRev)
|
||||||
|
@ -200,20 +194,20 @@ Pad.prototype.setText = function setText(newText) {
|
||||||
Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) {
|
Pad.prototype.appendChatMessage = function appendChatMessage(text, userId, time) {
|
||||||
this.chatHead++;
|
this.chatHead++;
|
||||||
//save the chat entry in the database
|
//save the chat entry in the database
|
||||||
db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time});
|
this.db.set("pad:"+this.id+":chat:"+this.chatHead, {"text": text, "userId": userId, "time": time});
|
||||||
//save the new chat head
|
//save the new chat head
|
||||||
db.setSub("pad:"+this.id, ["chatHead"], this.chatHead);
|
this.db.setSub("pad:"+this.id, ["chatHead"], this.chatHead);
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.getChatMessage = function getChatMessage(entryNum, callback) {
|
Pad.prototype.getChatMessage = function getChatMessage(entryNum, callback) {
|
||||||
var _this = this;
|
var that = this;
|
||||||
var entry;
|
var entry;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the chat entry
|
//get the chat entry
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
db.get("pad:"+_this.id+":chat:"+entryNum, function(err, _entry)
|
that.db.get("pad:"+that.id+":chat:"+entryNum, function(err, _entry)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
entry = _entry;
|
entry = _entry;
|
||||||
|
@ -231,7 +225,7 @@ Pad.prototype.getChatMessage = function getChatMessage(entryNum, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the authorName
|
//get the authorName
|
||||||
authorManager.getAuthorName(entry.userId, function(err, authorName)
|
that.authorManager.getAuthorName(entry.userId, function(err, authorName)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
entry.userName = authorName;
|
entry.userName = authorName;
|
||||||
|
@ -253,7 +247,7 @@ Pad.prototype.getLastChatMessages = function getLastChatMessages(count, callback
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var _this = this;
|
var that = this;
|
||||||
|
|
||||||
//works only if we decrement the amount, for some reason
|
//works only if we decrement the amount, for some reason
|
||||||
count--;
|
count--;
|
||||||
|
@ -279,7 +273,7 @@ Pad.prototype.getLastChatMessages = function getLastChatMessages(count, callback
|
||||||
var entries = [];
|
var entries = [];
|
||||||
async.forEach(neededEntries, function(entryObject, callback)
|
async.forEach(neededEntries, function(entryObject, callback)
|
||||||
{
|
{
|
||||||
_this.getChatMessage(entryObject.entryNum, function(err, entry)
|
that.getChatMessage(entryObject.entryNum, function(err, entry)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
entries[entryObject.order] = entry;
|
entries[entryObject.order] = entry;
|
||||||
|
@ -298,7 +292,7 @@ Pad.prototype.getLastChatMessages = function getLastChatMessages(count, callback
|
||||||
if(entries[i]!=null)
|
if(entries[i]!=null)
|
||||||
cleanedEntries.push(entries[i]);
|
cleanedEntries.push(entries[i]);
|
||||||
else
|
else
|
||||||
console.warn("WARNING: Found broken chat entry in pad " + _this.id);
|
console.warn("WARNING: Found broken chat entry in pad " + that.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, cleanedEntries);
|
callback(null, cleanedEntries);
|
||||||
|
@ -306,50 +300,45 @@ Pad.prototype.getLastChatMessages = function getLastChatMessages(count, callback
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.init = function init(text, callback) {
|
Pad.prototype.init = function init(text, callback) {
|
||||||
var _this = this;
|
var that = this;
|
||||||
|
|
||||||
//replace text with default text if text isn't set
|
|
||||||
if(text == null)
|
|
||||||
{
|
|
||||||
text = settings.defaultPadText;
|
|
||||||
}
|
|
||||||
|
|
||||||
//try to load the pad
|
//try to load the pad
|
||||||
db.get("pad:"+this.id, function(err, value)
|
this.db.get("pad:"+this.id, function(err, value)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//if this pad exists, load it
|
//if this pad exists, load it
|
||||||
if(value != null)
|
if(value != null)
|
||||||
{
|
{
|
||||||
_this.head = value.head;
|
that.head = value.head;
|
||||||
_this.atext = value.atext;
|
that.atext = value.atext;
|
||||||
_this.pool = _this.pool.fromJsonable(value.pool);
|
that.pool = that.pool.fromJsonable(value.pool);
|
||||||
|
|
||||||
//ensure we have a local chatHead variable
|
//ensure we have a local chatHead variable
|
||||||
if(value.chatHead != null)
|
if(value.chatHead != null)
|
||||||
_this.chatHead = value.chatHead;
|
that.chatHead = value.chatHead;
|
||||||
else
|
else
|
||||||
_this.chatHead = -1;
|
that.chatHead = -1;
|
||||||
|
|
||||||
//ensure we have a local publicStatus variable
|
//ensure we have a local publicStatus variable
|
||||||
if(value.publicStatus != null)
|
if(value.publicStatus != null)
|
||||||
_this.publicStatus = value.publicStatus;
|
that.publicStatus = value.publicStatus;
|
||||||
else
|
else
|
||||||
_this.publicStatus = false;
|
that.publicStatus = false;
|
||||||
|
|
||||||
//ensure we have a local passwordHash variable
|
//ensure we have a local passwordHash variable
|
||||||
if(value.passwordHash != null)
|
if(value.passwordHash != null)
|
||||||
_this.passwordHash = value.passwordHash;
|
that.passwordHash = value.passwordHash;
|
||||||
else
|
else
|
||||||
_this.passwordHash = null;
|
that.passwordHash = null;
|
||||||
}
|
}
|
||||||
//this pad doesn't exist, so create it
|
//this pad doesn't exist, so create it
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var firstChangeset = Changeset.makeSplice("\n", 0, 0, exports.cleanText(text));
|
var firstChangeset = Changeset.makeSplice("\n", 0, 0, exports.cleanText(text));
|
||||||
|
|
||||||
_this.appendRevision(firstChangeset, '');
|
that.appendRevision(firstChangeset, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null);
|
callback(null);
|
||||||
|
@ -358,10 +347,11 @@ Pad.prototype.init = function init(text, callback) {
|
||||||
|
|
||||||
Pad.prototype.remove = function remove(callback) {
|
Pad.prototype.remove = function remove(callback) {
|
||||||
var padID = this.id;
|
var padID = this.id;
|
||||||
var _this = this;
|
var that = this;
|
||||||
|
|
||||||
//kick everyone from this pad
|
//kick everyone from this pad
|
||||||
padMessageHandler.kickSessionsFromPad(padID);
|
//FIXME do this in the api or somewhere else
|
||||||
|
//padMessageHandler.kickSessionsFromPad(padID);
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//delete all relations
|
//delete all relations
|
||||||
|
@ -376,7 +366,7 @@ Pad.prototype.remove = function remove(callback) {
|
||||||
{
|
{
|
||||||
var groupID = padID.substring(0,padID.indexOf("$"));
|
var groupID = padID.substring(0,padID.indexOf("$"));
|
||||||
|
|
||||||
db.get("group:" + groupID, function (err, group)
|
that.db.get("group:" + groupID, function (err, group)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -384,7 +374,7 @@ Pad.prototype.remove = function remove(callback) {
|
||||||
delete group.pads[padID];
|
delete group.pads[padID];
|
||||||
|
|
||||||
//set the new value
|
//set the new value
|
||||||
db.set("group:" + groupID, group);
|
that.db.set("group:" + groupID, group);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
@ -398,12 +388,12 @@ Pad.prototype.remove = function remove(callback) {
|
||||||
//remove the readonly entries
|
//remove the readonly entries
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
readOnlyManager.getReadOnlyId(padID, function(err, readonlyID)
|
that.readOnlyManager.getReadOnlyId(padID, function(err, readonlyID)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
db.remove("pad2readonly:" + padID);
|
that.db.remove("pad2readonly:" + padID);
|
||||||
db.remove("readonly2pad:" + readonlyID);
|
that.db.remove("readonly2pad:" + readonlyID);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
@ -411,11 +401,11 @@ Pad.prototype.remove = function remove(callback) {
|
||||||
//delete all chat messages
|
//delete all chat messages
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var chatHead = _this.chatHead;
|
var chatHead = that.chatHead;
|
||||||
|
|
||||||
for(var i=0;i<=chatHead;i++)
|
for(var i=0;i<=chatHead;i++)
|
||||||
{
|
{
|
||||||
db.remove("pad:"+padID+":chat:"+i);
|
that.db.remove("pad:"+padID+":chat:"+i);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
|
@ -423,11 +413,11 @@ Pad.prototype.remove = function remove(callback) {
|
||||||
//delete all revisions
|
//delete all revisions
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
var revHead = _this.head;
|
var revHead = that.head;
|
||||||
|
|
||||||
for(var i=0;i<=revHead;i++)
|
for(var i=0;i<=revHead;i++)
|
||||||
{
|
{
|
||||||
db.remove("pad:"+padID+":revs:"+i);
|
that.db.remove("pad:"+padID+":revs:"+i);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
|
@ -437,8 +427,8 @@ Pad.prototype.remove = function remove(callback) {
|
||||||
//delete the pad entry and delete pad from padManager
|
//delete the pad entry and delete pad from padManager
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
db.remove("pad:"+padID);
|
that.db.remove("pad:"+padID);
|
||||||
padManager.unloadPad(padID);
|
that.padManager.unloadPad(padID);
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
|
@ -450,12 +440,12 @@ Pad.prototype.remove = function remove(callback) {
|
||||||
//set in db
|
//set in db
|
||||||
Pad.prototype.setPublicStatus = function setPublicStatus(publicStatus) {
|
Pad.prototype.setPublicStatus = function setPublicStatus(publicStatus) {
|
||||||
this.publicStatus = publicStatus;
|
this.publicStatus = publicStatus;
|
||||||
db.setSub("pad:"+this.id, ["publicStatus"], this.publicStatus);
|
this.db.setSub("pad:"+this.id, ["publicStatus"], this.publicStatus);
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.setPassword = function setPassword(password) {
|
Pad.prototype.setPassword = function setPassword(password) {
|
||||||
this.passwordHash = password == null ? null : hash(password, generateSalt());
|
this.passwordHash = password == null ? null : hash(password, generateSalt());
|
||||||
db.setSub("pad:"+this.id, ["passwordHash"], this.passwordHash);
|
this.db.setSub("pad:"+this.id, ["passwordHash"], this.passwordHash);
|
||||||
};
|
};
|
||||||
|
|
||||||
Pad.prototype.isCorrectPassword = function isCorrectPassword(password) {
|
Pad.prototype.isCorrectPassword = function isCorrectPassword(password) {
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var customError = require("../utils/customError");
|
var customError = require("../utils/customError");
|
||||||
var Pad = require("../db/Pad").Pad;
|
var Pad = require("../db/Pad").Pad;
|
||||||
var db = require("./DB").db;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An Object containing all known Pads. Provides "get" and "set" functions,
|
* An Object containing all known Pads. Provides "get" and "set" functions,
|
||||||
|
@ -32,12 +31,23 @@ var db = require("./DB").db;
|
||||||
* If this is needed in other places, it would be wise to make this a prototype
|
* If this is needed in other places, it would be wise to make this a prototype
|
||||||
* that's defined somewhere more sensible.
|
* that's defined somewhere more sensible.
|
||||||
*/
|
*/
|
||||||
var globalPads = {
|
|
||||||
|
var PadManager = function PadManager(settings, db, authorManager, readOnlyManager) {
|
||||||
|
this.settings = settings;
|
||||||
|
this.db = db;
|
||||||
|
this.authorManager = authorManager;
|
||||||
|
this.readOnlyManager = readOnlyManager;
|
||||||
|
|
||||||
|
this.globalPads = {
|
||||||
get: function (name) { return this[':'+name]; },
|
get: function (name) { return this[':'+name]; },
|
||||||
set: function (name, value) { this[':'+name] = value; },
|
set: function (name, value) { this[':'+name] = value; },
|
||||||
remove: function (name) { delete this[':'+name]; }
|
remove: function (name) { delete this[':'+name]; }
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
exports.PadManager = PadManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array of padId transformations. These represent changes in pad name policy over
|
* An array of padId transformations. These represent changes in pad name policy over
|
||||||
* time, and allow us to "play back" these changes so legacy padIds can be found.
|
* time, and allow us to "play back" these changes so legacy padIds can be found.
|
||||||
|
@ -51,10 +61,13 @@ var padIdTransforms = [
|
||||||
* @param id A String with the id of the pad
|
* @param id A String with the id of the pad
|
||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
*/
|
*/
|
||||||
exports.getPad = function(id, text, callback)
|
PadManager.prototype.getPad = function getPad(id, text, callback)
|
||||||
{
|
{
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
//TODO remove api specific shit
|
||||||
//check if this is a valid padId
|
//check if this is a valid padId
|
||||||
if(!exports.isValidPadId(id))
|
if(!this.isValidPadId(id))
|
||||||
{
|
{
|
||||||
callback(new customError(id + " is not a valid padId","apierror"));
|
callback(new customError(id + " is not a valid padId","apierror"));
|
||||||
return;
|
return;
|
||||||
|
@ -85,41 +98,49 @@ exports.getPad = function(id, text, callback)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var pad = globalPads.get(id);
|
var pad = this.globalPads.get(id);
|
||||||
|
|
||||||
//return pad if its already loaded
|
//return pad if its already loaded
|
||||||
if(pad != null)
|
if(pad != null) {
|
||||||
{
|
|
||||||
callback(null, pad);
|
callback(null, pad);
|
||||||
}
|
}
|
||||||
//try to load pad
|
//try to load pad
|
||||||
else
|
else {
|
||||||
{
|
|
||||||
pad = new Pad(id);
|
pad = new Pad(id);
|
||||||
|
|
||||||
|
//add some references
|
||||||
|
//TODO is this realy nice?
|
||||||
|
pad.padManager = this;
|
||||||
|
pad.db = this.db;
|
||||||
|
pad.readOnlyManager = this.readOnlyManager;
|
||||||
|
pad.authorManager = this.authorManager;
|
||||||
|
|
||||||
//initalize the pad
|
//initalize the pad
|
||||||
pad.init(text, function(err)
|
if(!text) {
|
||||||
{
|
text = this.settings.defaultPadText;
|
||||||
|
}
|
||||||
|
pad.init(text, function(err) {
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
globalPads.set(id, pad);
|
that.globalPads.set(id, pad);
|
||||||
callback(null, pad);
|
callback(null, pad);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
//checks if a pad exists
|
//checks if a pad exists
|
||||||
exports.doesPadExists = function(padId, callback)
|
PadManager.prototype.doesPadExists = function doesPadExists(padId, callback)
|
||||||
{
|
{
|
||||||
db.get("pad:"+padId, function(err, value)
|
this.db.get("pad:"+padId, function(err, value)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, value != null);
|
callback(null, value != null);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
//returns a sanitized padId, respecting legacy pad id formats
|
//returns a sanitized padId, respecting legacy pad id formats
|
||||||
exports.sanitizePadId = function(padId, callback) {
|
PadManager.prototype.sanitizePadId = function sanitizePadId(padId, callback) {
|
||||||
|
var that = this;
|
||||||
var transform_index = arguments[2] || 0;
|
var transform_index = arguments[2] || 0;
|
||||||
//we're out of possible transformations, so just return it
|
//we're out of possible transformations, so just return it
|
||||||
if(transform_index >= padIdTransforms.length)
|
if(transform_index >= padIdTransforms.length)
|
||||||
|
@ -129,7 +150,7 @@ exports.sanitizePadId = function(padId, callback) {
|
||||||
//check if padId exists
|
//check if padId exists
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
exports.doesPadExists(padId, function(junk, exists)
|
this.doesPadExists(padId, function(junk, exists)
|
||||||
{
|
{
|
||||||
if(exists)
|
if(exists)
|
||||||
{
|
{
|
||||||
|
@ -145,20 +166,18 @@ exports.sanitizePadId = function(padId, callback) {
|
||||||
transform_index += 1;
|
transform_index += 1;
|
||||||
}
|
}
|
||||||
//check the next transform
|
//check the next transform
|
||||||
exports.sanitizePadId(transformedPadId, callback, transform_index);
|
that.sanitizePadId(transformedPadId, callback, transform_index);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.isValidPadId = function(padId)
|
PadManager.prototype.isValidPadId = function(padId) {
|
||||||
{
|
return (/^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/).test(padId);
|
||||||
return /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);
|
};
|
||||||
}
|
|
||||||
|
|
||||||
//removes a pad from the array
|
//removes a pad from the array
|
||||||
exports.unloadPad = function(padId)
|
Pad.prototype.unloadPad = function unloadPad(padId) {
|
||||||
{
|
if(this.globalPads.get(padId))
|
||||||
if(globalPads.get(padId))
|
this.globalPads.remove(padId);
|
||||||
globalPads.remove(padId);
|
};
|
||||||
}
|
|
||||||
|
|
|
@ -19,24 +19,31 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var db = require("./DB").db;
|
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
|
|
||||||
var randomString = require("../utils/randomstring");
|
var randomString = require("../utils/randomstring");
|
||||||
|
|
||||||
|
|
||||||
|
var ReadOnlyManager = function ReadOnlyManager(db) {
|
||||||
|
this.db = db;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.ReadOnlyManager = ReadOnlyManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a read only id for a pad
|
* returns a read only id for a pad
|
||||||
* @param {String} padId the id of the pad
|
* @param {String} padId the id of the pad
|
||||||
*/
|
*/
|
||||||
exports.getReadOnlyId = function (padId, callback)
|
ReadOnlyManager.prototype.getReadOnlyId = function getReadOnlyId(padId, callback) {
|
||||||
{
|
var that = this;
|
||||||
|
|
||||||
var readOnlyId;
|
var readOnlyId;
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
//check if there is a pad2readonly entry
|
//check if there is a pad2readonly entry
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
db.get("pad2readonly:" + padId, callback);
|
that.db.get("pad2readonly:" + padId, callback);
|
||||||
},
|
},
|
||||||
function(dbReadOnlyId, callback)
|
function(dbReadOnlyId, callback)
|
||||||
{
|
{
|
||||||
|
@ -45,8 +52,8 @@ exports.getReadOnlyId = function (padId, callback)
|
||||||
{
|
{
|
||||||
readOnlyId = "r." + randomString(16);
|
readOnlyId = "r." + randomString(16);
|
||||||
|
|
||||||
db.set("pad2readonly:" + padId, readOnlyId);
|
that.db.set("pad2readonly:" + padId, readOnlyId);
|
||||||
db.set("readonly2pad:" + readOnlyId, padId);
|
that.db.set("readonly2pad:" + readOnlyId, padId);
|
||||||
}
|
}
|
||||||
//there is a readOnly Entry in the database, let's take this one
|
//there is a readOnly Entry in the database, let's take this one
|
||||||
else
|
else
|
||||||
|
@ -61,14 +68,13 @@ exports.getReadOnlyId = function (padId, callback)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
//return the results
|
//return the results
|
||||||
callback(null, readOnlyId);
|
callback(null, readOnlyId);
|
||||||
})
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a the padId for a read only id
|
* returns a the padId for a read only id
|
||||||
* @param {String} readOnlyId read only id
|
* @param {String} readOnlyId read only id
|
||||||
*/
|
*/
|
||||||
exports.getPadId = function(readOnlyId, callback)
|
ReadOnlyManager.prototype.getPadId = function(readOnlyId, callback) {
|
||||||
{
|
this.db.get("readonly2pad:" + readOnlyId, callback);
|
||||||
db.get("readonly2pad:" + readOnlyId, callback);
|
};
|
||||||
}
|
|
||||||
|
|
|
@ -19,15 +19,20 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var db = require("./DB").db;
|
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var authorManager = require("./AuthorManager");
|
|
||||||
var padManager = require("./PadManager");
|
|
||||||
var sessionManager = require("./SessionManager");
|
|
||||||
var settings = require("../utils/Settings")
|
|
||||||
|
|
||||||
var randomString = require("../utils/randomstring");
|
var randomString = require("../utils/randomstring");
|
||||||
|
|
||||||
|
var SecurityManager = function SecurityManager(settings, db, authorManager, padManager, sessionManager) {
|
||||||
|
this.db = db;
|
||||||
|
this.settings = settings;
|
||||||
|
this.authorManager = authorManager;
|
||||||
|
this.padManager = padManager;
|
||||||
|
this.sessionManager = sessionManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.SecurityManager = SecurityManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function controlls the access to a pad, it checks if the user can access a pad.
|
* This function controlls the access to a pad, it checks if the user can access a pad.
|
||||||
* @param padID the pad the user wants to access
|
* @param padID the pad the user wants to access
|
||||||
|
@ -36,12 +41,13 @@ var randomString = require("../utils/randomstring");
|
||||||
* @param password the password the user has given to access this pad, can be null
|
* @param password the password the user has given to access this pad, can be null
|
||||||
* @param callback will be called with (err, {accessStatus: grant|deny|wrongPassword|needPassword, authorID: a.xxxxxx})
|
* @param callback will be called with (err, {accessStatus: grant|deny|wrongPassword|needPassword, authorID: a.xxxxxx})
|
||||||
*/
|
*/
|
||||||
exports.checkAccess = function (padID, sessionID, token, password, callback)
|
SecurityManager.prototype.checkAccess = function checkAccess(padID, sessionID, token, password, callback)
|
||||||
{
|
{
|
||||||
|
var that = this;
|
||||||
var statusObject;
|
var statusObject;
|
||||||
|
|
||||||
// a valid session is required (api-only mode)
|
// a valid session is required (api-only mode)
|
||||||
if(settings.requireSession)
|
if(this.settings.requireSession)
|
||||||
{
|
{
|
||||||
// no sessionID, access is denied
|
// no sessionID, access is denied
|
||||||
if(!sessionID)
|
if(!sessionID)
|
||||||
|
@ -57,17 +63,17 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
if(padID.indexOf("$") == -1)
|
if(padID.indexOf("$") == -1)
|
||||||
{
|
{
|
||||||
//get author for this token
|
//get author for this token
|
||||||
authorManager.getAuthor4Token(token, function(err, author)
|
this.authorManager.getAuthor4Token(token, function(err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
// assume user has access
|
// assume user has access
|
||||||
statusObject = {accessStatus: "grant", authorID: author};
|
statusObject = {accessStatus: "grant", authorID: author};
|
||||||
// user can't create pads
|
// user can't create pads
|
||||||
if(settings.editOnly)
|
if(that.settings.editOnly)
|
||||||
{
|
{
|
||||||
// check if pad exists
|
// check if pad exists
|
||||||
padManager.doesPadExists(padID, function(err, exists)
|
that.padManager.doesPadExists(padID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -107,7 +113,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
//does pad exists
|
//does pad exists
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
padManager.doesPadExists(padID, function(err, exists)
|
that.padManager.doesPadExists(padID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
padExists = exists;
|
padExists = exists;
|
||||||
|
@ -117,7 +123,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
//get informations about this session
|
//get informations about this session
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
sessionManager.getSessionInfo(sessionID, function(err, sessionInfo)
|
that.sessionManager.getSessionInfo(sessionID, function(err, sessionInfo)
|
||||||
{
|
{
|
||||||
//skip session validation if the session doesn't exists
|
//skip session validation if the session doesn't exists
|
||||||
if(err && err.message == "sessionID does not exist")
|
if(err && err.message == "sessionID does not exist")
|
||||||
|
@ -145,7 +151,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//get author for this token
|
//get author for this token
|
||||||
authorManager.getAuthor4Token(token, function(err, author)
|
that.authorManager.getAuthor4Token(token, function(err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
tokenAuthor = author;
|
tokenAuthor = author;
|
||||||
|
@ -164,7 +170,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
padManager.getPad(padID, function(err, pad)
|
that.padManager.getPad(padID, function(err, pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -223,7 +229,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
//--> grant access
|
//--> grant access
|
||||||
statusObject = {accessStatus: "grant", authorID: sessionAuthor};
|
statusObject = {accessStatus: "grant", authorID: sessionAuthor};
|
||||||
//--> deny access if user isn't allowed to create the pad
|
//--> deny access if user isn't allowed to create the pad
|
||||||
if(settings.editOnly) statusObject.accessStatus = "deny";
|
if(that.settings.editOnly) statusObject.accessStatus = "deny";
|
||||||
}
|
}
|
||||||
// there is no valid session avaiable AND pad exists
|
// there is no valid session avaiable AND pad exists
|
||||||
else if(!validSession && padExists)
|
else if(!validSession && padExists)
|
||||||
|
@ -277,4 +283,4 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, statusObject);
|
callback(null, statusObject);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
|
@ -21,33 +21,40 @@
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var customError = require("../utils/customError");
|
var customError = require("../utils/customError");
|
||||||
var randomString = require("../utils/randomstring");
|
var randomString = require("../utils/randomstring");
|
||||||
var db = require("./DB").db;
|
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var groupMangager = require("./GroupManager");
|
|
||||||
var authorMangager = require("./AuthorManager");
|
|
||||||
|
|
||||||
exports.doesSessionExist = function(sessionID, callback)
|
|
||||||
|
var SessionManager = function SessionManager(db, groupManager, authorManager) {
|
||||||
|
this.db = db;
|
||||||
|
this.groupManager = groupManager;
|
||||||
|
this.authorManager = authorManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.SessionManager = SessionManager;
|
||||||
|
|
||||||
|
SessionManager.prototype.doesSessionExist = function(sessionID, callback)
|
||||||
{
|
{
|
||||||
//check if the database entry of this session exists
|
//check if the database entry of this session exists
|
||||||
db.get("session:" + sessionID, function (err, session)
|
this.db.get("session:" + sessionID, function (err, session)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback(null, session != null);
|
callback(null, session != null);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new session between an author and a group
|
* Creates a new session between an author and a group
|
||||||
*/
|
*/
|
||||||
exports.createSession = function(groupID, authorID, validUntil, callback)
|
SessionManager.prototype.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
{
|
{
|
||||||
var sessionID;
|
var sessionID;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//check if group exists
|
//check if group exists
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
groupMangager.doesGroupExist(groupID, function(err, exists)
|
that.groupMangager.doesGroupExist(groupID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -66,7 +73,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
//check if author exists
|
//check if author exists
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
authorMangager.doesAuthorExists(authorID, function(err, exists)
|
that.authorMangager.doesAuthorExists(authorID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -125,7 +132,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
sessionID = "s." + randomString(16);
|
sessionID = "s." + randomString(16);
|
||||||
|
|
||||||
//set the session into the database
|
//set the session into the database
|
||||||
db.set("session:" + sessionID, {"groupID": groupID, "authorID": authorID, "validUntil": validUntil});
|
that.db.set("session:" + sessionID, {"groupID": groupID, "authorID": authorID, "validUntil": validUntil});
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
},
|
},
|
||||||
|
@ -133,7 +140,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//get the entry
|
//get the entry
|
||||||
db.get("group2sessions:" + groupID, function(err, group2sessions)
|
that.db.get("group2sessions:" + groupID, function(err, group2sessions)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -147,7 +154,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
group2sessions.sessionIDs[sessionID] = 1;
|
group2sessions.sessionIDs[sessionID] = 1;
|
||||||
|
|
||||||
//save the new element back
|
//save the new element back
|
||||||
db.set("group2sessions:" + groupID, group2sessions);
|
that.db.set("group2sessions:" + groupID, group2sessions);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
@ -156,7 +163,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//get the entry
|
//get the entry
|
||||||
db.get("author2sessions:" + authorID, function(err, author2sessions)
|
that.db.get("author2sessions:" + authorID, function(err, author2sessions)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -170,7 +177,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
author2sessions.sessionIDs[sessionID] = 1;
|
author2sessions.sessionIDs[sessionID] = 1;
|
||||||
|
|
||||||
//save the new element back
|
//save the new element back
|
||||||
db.set("author2sessions:" + authorID, author2sessions);
|
that.db.set("author2sessions:" + authorID, author2sessions);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
|
@ -184,17 +191,17 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getSessionInfo = function(sessionID, callback)
|
SessionManager.prototype.getSessionInfo = function(sessionID, callback)
|
||||||
{
|
{
|
||||||
//check if the database entry of this session exists
|
//check if the database entry of this session exists
|
||||||
db.get("session:" + sessionID, function (err, session)
|
this.db.get("session:" + sessionID, function (err, session)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
//session does not exists
|
//session does not exists
|
||||||
if(session == null)
|
if(session == null)
|
||||||
{
|
{
|
||||||
callback(new customError("sessionID does not exist","apierror"))
|
callback(new customError("sessionID does not exist","apierror"));
|
||||||
}
|
}
|
||||||
//everything is fine, return the sessioninfos
|
//everything is fine, return the sessioninfos
|
||||||
else
|
else
|
||||||
|
@ -202,7 +209,7 @@ exports.getSessionInfo = function(sessionID, callback)
|
||||||
callback(null, session);
|
callback(null, session);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a session
|
* Deletes a session
|
||||||
|
@ -211,12 +218,13 @@ exports.deleteSession = function(sessionID, callback)
|
||||||
{
|
{
|
||||||
var authorID, groupID;
|
var authorID, groupID;
|
||||||
var group2sessions, author2sessions;
|
var group2sessions, author2sessions;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//get the session entry
|
//get the session entry
|
||||||
db.get("session:" + sessionID, function (err, session)
|
that.db.get("session:" + sessionID, function (err, session)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -238,7 +246,7 @@ exports.deleteSession = function(sessionID, callback)
|
||||||
//get the group2sessions entry
|
//get the group2sessions entry
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
db.get("group2sessions:" + groupID, function (err, _group2sessions)
|
that.db.get("group2sessions:" + groupID, function (err, _group2sessions)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
group2sessions = _group2sessions;
|
group2sessions = _group2sessions;
|
||||||
|
@ -248,7 +256,7 @@ exports.deleteSession = function(sessionID, callback)
|
||||||
//get the author2sessions entry
|
//get the author2sessions entry
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
db.get("author2sessions:" + authorID, function (err, _author2sessions)
|
that.db.get("author2sessions:" + authorID, function (err, _author2sessions)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
author2sessions = _author2sessions;
|
author2sessions = _author2sessions;
|
||||||
|
@ -259,15 +267,15 @@ exports.deleteSession = function(sessionID, callback)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//remove the session
|
//remove the session
|
||||||
db.remove("session:" + sessionID);
|
that.db.remove("session:" + sessionID);
|
||||||
|
|
||||||
//remove session from group2sessions
|
//remove session from group2sessions
|
||||||
delete group2sessions.sessionIDs[sessionID];
|
delete group2sessions.sessionIDs[sessionID];
|
||||||
db.set("group2sessions:" + groupID, group2sessions);
|
that.db.set("group2sessions:" + groupID, group2sessions);
|
||||||
|
|
||||||
//remove session from author2sessions
|
//remove session from author2sessions
|
||||||
delete author2sessions.sessionIDs[sessionID];
|
delete author2sessions.sessionIDs[sessionID];
|
||||||
db.set("author2sessions:" + authorID, author2sessions);
|
that.db.set("author2sessions:" + authorID, author2sessions);
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
@ -276,11 +284,12 @@ exports.deleteSession = function(sessionID, callback)
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
callback();
|
callback();
|
||||||
})
|
})
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.listSessionsOfGroup = function(groupID, callback)
|
SessionManager.prototype.listSessionsOfGroup = function(groupID, callback)
|
||||||
{
|
{
|
||||||
groupMangager.doesGroupExist(groupID, function(err, exists)
|
var that = this;
|
||||||
|
this.groupMangager.doesGroupExist(groupID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -292,14 +301,15 @@ exports.listSessionsOfGroup = function(groupID, callback)
|
||||||
//everything is fine, continue
|
//everything is fine, continue
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listSessionsWithDBKey("group2sessions:" + groupID, callback);
|
that.listSessionsWithDBKey("group2sessions:" + groupID, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.listSessionsOfAuthor = function(authorID, callback)
|
SessionManager.prototype.listSessionsOfAuthor = function(authorID, callback)
|
||||||
{
|
{
|
||||||
authorMangager.doesAuthorExists(authorID, function(err, exists)
|
var that = this;
|
||||||
|
this.authorMangager.doesAuthorExists(authorID, function(err, exists)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -311,21 +321,21 @@ exports.listSessionsOfAuthor = function(authorID, callback)
|
||||||
//everything is fine, continue
|
//everything is fine, continue
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
listSessionsWithDBKey("author2sessions:" + authorID, callback);
|
that.listSessionsWithDBKey("author2sessions:" + authorID, callback);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
//this function is basicly the code listSessionsOfAuthor and listSessionsOfGroup has in common
|
//this function is basicly the code listSessionsOfAuthor and listSessionsOfGroup has in common
|
||||||
function listSessionsWithDBKey (dbkey, callback)
|
SessionManager.prototype.listSessionsWithDBKey = function listSessionsWithDBKey (dbkey, callback)
|
||||||
{
|
{
|
||||||
var sessions;
|
var sessions;
|
||||||
|
var that = this;
|
||||||
async.series([
|
async.series([
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//get the group2sessions entry
|
//get the group2sessions entry
|
||||||
db.get(dbkey, function(err, sessionObject)
|
that.db.get(dbkey, function(err, sessionObject)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
sessions = sessionObject ? sessionObject.sessionIDs : null;
|
sessions = sessionObject ? sessionObject.sessionIDs : null;
|
||||||
|
@ -344,7 +354,7 @@ function listSessionsWithDBKey (dbkey, callback)
|
||||||
//foreach trough the sessions and get the sessioninfos
|
//foreach trough the sessions and get the sessioninfos
|
||||||
async.forEach(sessionIDs, function(sessionID, callback)
|
async.forEach(sessionIDs, function(sessionID, callback)
|
||||||
{
|
{
|
||||||
exports.getSessionInfo(sessionID, function(err, sessionInfo)
|
that.getSessionInfo(sessionID, function(err, sessionInfo)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
sessions[sessionID] = sessionInfo;
|
sessions[sessionID] = sessionInfo;
|
||||||
|
@ -360,7 +370,6 @@ function listSessionsWithDBKey (dbkey, callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
//checks if a number is an int
|
//checks if a number is an int
|
||||||
function is_int(value)
|
function is_int(value) {
|
||||||
{
|
return (parseFloat(value) == parseInt(value, 10)) && !isNaN(value);
|
||||||
return (parseFloat(value) == parseInt(value)) && !isNaN(value)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,92 +20,101 @@
|
||||||
|
|
||||||
var ERR = require("async-stacktrace");
|
var ERR = require("async-stacktrace");
|
||||||
var async = require("async");
|
var async = require("async");
|
||||||
var padManager = require("../db/PadManager");
|
|
||||||
var Changeset = require("../utils/Changeset");
|
var Changeset = require("../utils/Changeset");
|
||||||
var AttributePoolFactory = require("../utils/AttributePoolFactory");
|
var AttributePoolFactory = require("../utils/AttributePoolFactory");
|
||||||
var authorManager = require("../db/AuthorManager");
|
|
||||||
var readOnlyManager = require("../db/ReadOnlyManager");
|
|
||||||
var settings = require('../utils/Settings');
|
|
||||||
var securityManager = require("../db/SecurityManager");
|
|
||||||
var log4js = require('log4js');
|
var log4js = require('log4js');
|
||||||
var messageLogger = log4js.getLogger("message");
|
var messageLogger = log4js.getLogger("message");
|
||||||
|
|
||||||
/**
|
|
||||||
|
var PadMessageHandler = function(settings, padManager, authorManager, readOnlyManager, securityManager) {
|
||||||
|
this.settings = settings;
|
||||||
|
this.padManager = padManager;
|
||||||
|
this.authorManager = authorManager;
|
||||||
|
this.readOnlyManager = readOnlyManager;
|
||||||
|
this.securityManager = securityManager;
|
||||||
|
|
||||||
|
/**
|
||||||
* A associative array that translates a session to a pad
|
* A associative array that translates a session to a pad
|
||||||
*/
|
*/
|
||||||
var session2pad = {};
|
this.session2pad = {};
|
||||||
/**
|
/**
|
||||||
* A associative array that saves which sessions belong to a pad
|
* A associative array that saves which sessions belong to a pad
|
||||||
*/
|
*/
|
||||||
var pad2sessions = {};
|
this.pad2sessions = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A associative array that saves some general informations about a session
|
* A associative array that saves some general informations about a session
|
||||||
* key = sessionId
|
* key = sessionId
|
||||||
* values = author, rev
|
* values = author, rev
|
||||||
* rev = That last revision that was send to this client
|
* rev = That last revision that was send to this client
|
||||||
* author = the author name of this session
|
* author = the author name of this session
|
||||||
*/
|
*/
|
||||||
var sessioninfos = {};
|
this.sessioninfos = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the Socket class we need to send and recieve data from the client
|
* Saves the Socket class we need to send and recieve data from the client
|
||||||
*/
|
*/
|
||||||
var socketio;
|
this.socketio = undefined;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.PadMessageHandler = PadMessageHandler;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Method is called by server.js to tell the message handler on which socket it should send
|
* This Method is called by server.js to tell the message handler on which socket it should send
|
||||||
* @param socket_io The Socket
|
* @param socket_io The Socket
|
||||||
*/
|
*/
|
||||||
exports.setSocketIO = function(socket_io)
|
PadMessageHandler.prototype.setSocketIO = function(socket_io)
|
||||||
{
|
{
|
||||||
socketio=socket_io;
|
this.socketio=socket_io;
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the connection of a new user
|
* Handles the connection of a new user
|
||||||
* @param client the new client
|
* @param client the new client
|
||||||
*/
|
*/
|
||||||
exports.handleConnect = function(client)
|
PadMessageHandler.prototype.handleConnect = function(client)
|
||||||
{
|
{
|
||||||
//Initalize session2pad and sessioninfos for this new session
|
//Initalize session2pad and sessioninfos for this new session
|
||||||
session2pad[client.id]=null;
|
this.session2pad[client.id]=null;
|
||||||
sessioninfos[client.id]={};
|
this.sessioninfos[client.id]={};
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kicks all sessions from a pad
|
* Kicks all sessions from a pad
|
||||||
* @param client the new client
|
* @param client the new client
|
||||||
*/
|
*/
|
||||||
exports.kickSessionsFromPad = function(padID)
|
PadMessageHandler.prototype.kickSessionsFromPad = function(padID)
|
||||||
{
|
{
|
||||||
//skip if there is nobody on this pad
|
//skip if there is nobody on this pad
|
||||||
if(!pad2sessions[padID])
|
if(!this.pad2sessions[padID])
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//disconnect everyone from this pad
|
//disconnect everyone from this pad
|
||||||
for(var i in pad2sessions[padID])
|
for(var i in this.pad2sessions[padID])
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[pad2sessions[padID][i]].json.send({disconnect:"deleted"});
|
this.socketio.sockets.sockets[this.pad2sessions[padID][i]].json.send({disconnect:"deleted"});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the disconnection of a user
|
* Handles the disconnection of a user
|
||||||
* @param client the client that leaves
|
* @param client the client that leaves
|
||||||
*/
|
*/
|
||||||
exports.handleDisconnect = function(client)
|
PadMessageHandler.prototype.handleDisconnect = function(client)
|
||||||
{
|
{
|
||||||
|
var that = this;
|
||||||
//save the padname of this session
|
//save the padname of this session
|
||||||
var sessionPad=session2pad[client.id];
|
var sessionPad = this.session2pad[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(sessioninfos[client.id] && sessioninfos[client.id].author)
|
if(this.sessioninfos[client.id] && this.sessioninfos[client.id].author)
|
||||||
{
|
{
|
||||||
var author = sessioninfos[client.id].author;
|
var author = this.sessioninfos[client.id].author;
|
||||||
|
|
||||||
//get the author color out of the db
|
//get the author color out of the db
|
||||||
authorManager.getAuthorColorId(author, function(err, color)
|
this.authorManager.getAuthorColorId(author, function(err, color)
|
||||||
{
|
{
|
||||||
ERR(err);
|
ERR(err);
|
||||||
|
|
||||||
|
@ -124,34 +133,34 @@ 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
|
||||||
for(i in pad2sessions[sessionPad])
|
for(var i in that.pad2sessions[sessionPad])
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[pad2sessions[sessionPad][i]].json.send(messageToTheOtherUsers);
|
that.socketio.sockets.sockets[that.pad2sessions[sessionPad][i]].json.send(messageToTheOtherUsers);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Go trough all sessions of this pad, search and destroy the entry of this client
|
//Go trough all sessions of this pad, search and destroy the entry of this client
|
||||||
for(i in pad2sessions[sessionPad])
|
for(var i in that.pad2sessions[sessionPad])
|
||||||
{
|
{
|
||||||
if(pad2sessions[sessionPad][i] == client.id)
|
if(that.pad2sessions[sessionPad][i] == client.id)
|
||||||
{
|
{
|
||||||
pad2sessions[sessionPad].splice(i, 1);
|
that.pad2sessions[sessionPad].splice(i, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Delete the session2pad and sessioninfos entrys of this session
|
//Delete the session2pad and sessioninfos entrys of this session
|
||||||
delete session2pad[client.id];
|
delete that.session2pad[client.id];
|
||||||
delete sessioninfos[client.id];
|
delete that.sessioninfos[client.id];
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a message from a user
|
* Handles a message from a user
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
exports.handleMessage = function(client, message)
|
PadMessageHandler.prototype.handleMessage = function(client, message)
|
||||||
{
|
{
|
||||||
if(message == null)
|
if(message == null)
|
||||||
{
|
{
|
||||||
|
@ -167,56 +176,58 @@ exports.handleMessage = function(client, message)
|
||||||
//Check what type of message we get and delegate to the other methodes
|
//Check what type of message we get and delegate to the other methodes
|
||||||
if(message.type == "CLIENT_READY")
|
if(message.type == "CLIENT_READY")
|
||||||
{
|
{
|
||||||
handleClientReady(client, message);
|
this.handleClientReady(client, message);
|
||||||
}
|
}
|
||||||
else if(message.type == "COLLABROOM" &&
|
else if(message.type == "COLLABROOM" &&
|
||||||
message.data.type == "USER_CHANGES")
|
message.data.type == "USER_CHANGES")
|
||||||
{
|
{
|
||||||
handleUserChanges(client, message);
|
this.handleUserChanges(client, message);
|
||||||
}
|
}
|
||||||
else if(message.type == "COLLABROOM" &&
|
else if(message.type == "COLLABROOM" &&
|
||||||
message.data.type == "USERINFO_UPDATE")
|
message.data.type == "USERINFO_UPDATE")
|
||||||
{
|
{
|
||||||
handleUserInfoUpdate(client, message);
|
this.handleUserInfoUpdate(client, message);
|
||||||
}
|
}
|
||||||
else if(message.type == "COLLABROOM" &&
|
else if(message.type == "COLLABROOM" &&
|
||||||
message.data.type == "CHAT_MESSAGE")
|
message.data.type == "CHAT_MESSAGE")
|
||||||
{
|
{
|
||||||
handleChatMessage(client, message);
|
this.handleChatMessage(client, message);
|
||||||
}
|
}
|
||||||
else if(message.type == "COLLABROOM" &&
|
else if(message.type == "COLLABROOM" &&
|
||||||
message.data.type == "CLIENT_MESSAGE" &&
|
message.data.type == "CLIENT_MESSAGE" &&
|
||||||
message.data.payload.type == "suggestUserName")
|
message.data.payload.type == "suggestUserName")
|
||||||
{
|
{
|
||||||
handleSuggestUserName(client, message);
|
this.handleSuggestUserName(client, message);
|
||||||
}
|
}
|
||||||
//if the message type is unknown, throw an exception
|
//if the message type is unknown, throw an exception
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
messageLogger.warn("Dropped message, unknown Message Type " + message.type);
|
messageLogger.warn("Dropped message, unknown Message Type " + message.type);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a Chat Message
|
* Handles a Chat Message
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function handleChatMessage(client, message)
|
PadMessageHandler.prototype.handleChatMessage = function handleChatMessage(client, message)
|
||||||
{
|
{
|
||||||
var time = new Date().getTime();
|
var time = new Date().getTime();
|
||||||
var userId = sessioninfos[client.id].author;
|
var userId = this.sessioninfos[client.id].author;
|
||||||
var text = message.data.text;
|
var text = message.data.text;
|
||||||
var padId = session2pad[client.id];
|
var padId = this.session2pad[client.id];
|
||||||
|
|
||||||
var pad;
|
var pad;
|
||||||
var userName;
|
var userName;
|
||||||
|
|
||||||
|
var that = this;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the pad
|
//get the pad
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(padId, function(err, _pad)
|
that.padManager.getPad(padId, function(err, _pad)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
pad = _pad;
|
pad = _pad;
|
||||||
|
@ -225,7 +236,7 @@ function handleChatMessage(client, message)
|
||||||
},
|
},
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
authorManager.getAuthorName(userId, function(err, _userName)
|
that.authorManager.getAuthorName(userId, function(err, _userName)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
userName = _userName;
|
userName = _userName;
|
||||||
|
@ -250,9 +261,9 @@ function handleChatMessage(client, message)
|
||||||
};
|
};
|
||||||
|
|
||||||
//broadcast the chat message to everyone on the pad
|
//broadcast the chat message to everyone on the pad
|
||||||
for(var i in pad2sessions[padId])
|
for(var i in that.pad2sessions[padId])
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[pad2sessions[padId][i]].json.send(msg);
|
that.socketio.sockets.sockets[that.pad2sessions[padId][i]].json.send(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
|
@ -261,7 +272,7 @@ function handleChatMessage(client, message)
|
||||||
{
|
{
|
||||||
ERR(err);
|
ERR(err);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -269,7 +280,7 @@ function handleChatMessage(client, message)
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function handleSuggestUserName(client, message)
|
PadMessageHandler.prototype.handleSuggestUserName = function handleSuggestUserName(client, message)
|
||||||
{
|
{
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(message.data.payload.newName == null)
|
if(message.data.payload.newName == null)
|
||||||
|
@ -283,25 +294,25 @@ function handleSuggestUserName(client, message)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var padId = session2pad[client.id];
|
var padId = this.session2pad[client.id];
|
||||||
|
|
||||||
//search the author and send him this message
|
//search the author and send him this message
|
||||||
for(var i in pad2sessions[padId])
|
for(var i in this.pad2sessions[padId])
|
||||||
{
|
{
|
||||||
if(sessioninfos[pad2sessions[padId][i]].author == message.data.payload.unnamedId)
|
if(this.sessioninfos[this.pad2sessions[padId][i]].author == message.data.payload.unnamedId)
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[pad2sessions[padId][i]].send(message);
|
this.socketio.sockets.sockets[this.pad2sessions[padId][i]].send(message);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a USERINFO_UPDATE, that means that a user have changed his color or name. Anyway, we get both informations
|
* Handles a USERINFO_UPDATE, that means that a user have changed his color or name. Anyway, we get both informations
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function handleUserInfoUpdate(client, message)
|
PadMessageHandler.prototype.handleUserInfoUpdate = function handleUserInfoUpdate(client, message)
|
||||||
{
|
{
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(message.data.userInfo.colorId == null)
|
if(message.data.userInfo.colorId == null)
|
||||||
|
@ -311,13 +322,13 @@ function handleUserInfoUpdate(client, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Find out the author name of this session
|
//Find out the author name of this session
|
||||||
var author = sessioninfos[client.id].author;
|
var author = this.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);
|
this.authorManager.setAuthorColorId(author, message.data.userInfo.colorId);
|
||||||
authorManager.setAuthorName(author, message.data.userInfo.name);
|
this.authorManager.setAuthorName(author, message.data.userInfo.name);
|
||||||
|
|
||||||
var padId = session2pad[client.id];
|
var padId = this.session2pad[client.id];
|
||||||
|
|
||||||
//set a null name, when there is no name set. cause the client wants it null
|
//set a null name, when there is no name set. cause the client wants it null
|
||||||
if(message.data.userInfo.name == null)
|
if(message.data.userInfo.name == null)
|
||||||
|
@ -329,14 +340,14 @@ function handleUserInfoUpdate(client, message)
|
||||||
message.data.type = "USER_NEWINFO";
|
message.data.type = "USER_NEWINFO";
|
||||||
|
|
||||||
//Send the other clients on the pad the update message
|
//Send the other clients on the pad the update message
|
||||||
for(var i in pad2sessions[padId])
|
for(var i in this.pad2sessions[padId])
|
||||||
{
|
{
|
||||||
if(pad2sessions[padId][i] != client.id)
|
if(this.pad2sessions[padId][i] != client.id)
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[pad2sessions[padId][i]].json.send(message);
|
this.socketio.sockets.sockets[this.pad2sessions[padId][i]].json.send(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a USERINFO_UPDATE, that means that a user have changed his color or name. Anyway, we get both informations
|
* Handles a USERINFO_UPDATE, that means that a user have changed his color or name. Anyway, we get both informations
|
||||||
|
@ -345,7 +356,7 @@ function handleUserInfoUpdate(client, message)
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function handleUserChanges(client, message)
|
PadMessageHandler.prototype.handleUserChanges = function handleUserChanges(client, message)
|
||||||
{
|
{
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(message.data.baseRev == null)
|
if(message.data.baseRev == null)
|
||||||
|
@ -370,12 +381,13 @@ function handleUserChanges(client, message)
|
||||||
var changeset = message.data.changeset;
|
var changeset = message.data.changeset;
|
||||||
|
|
||||||
var r, apool, pad;
|
var r, apool, pad;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//get the pad
|
//get the pad
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(session2pad[client.id], function(err, value)
|
that.padManager.getPad(that.session2pad[client.id], function(err, value)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
pad = value;
|
pad = value;
|
||||||
|
@ -448,7 +460,7 @@ function handleUserChanges(client, message)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var thisAuthor = sessioninfos[client.id].author;
|
var thisAuthor = that.sessioninfos[client.id].author;
|
||||||
|
|
||||||
pad.appendRevision(changeset, thisAuthor);
|
pad.appendRevision(changeset, thisAuthor);
|
||||||
|
|
||||||
|
@ -462,27 +474,28 @@ function handleUserChanges(client, message)
|
||||||
pad.appendRevision(nlChangeset);
|
pad.appendRevision(nlChangeset);
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.updatePadClients(pad, callback);
|
that.updatePadClients(pad, callback);
|
||||||
}
|
}
|
||||||
], function(err)
|
], function(err)
|
||||||
{
|
{
|
||||||
ERR(err);
|
ERR(err);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
exports.updatePadClients = function(pad, callback)
|
PadMessageHandler.prototype.updatePadClients = function(pad, callback)
|
||||||
{
|
{
|
||||||
|
var that = this;
|
||||||
//skip this step if noone is on this pad
|
//skip this step if noone is on this pad
|
||||||
if(!pad2sessions[pad.id])
|
if(!this.pad2sessions[pad.id])
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//go trough all sessions on this pad
|
//go trough all sessions on this pad
|
||||||
async.forEach(pad2sessions[pad.id], function(session, callback)
|
async.forEach(that.pad2sessions[pad.id], function(session, callback)
|
||||||
{
|
{
|
||||||
var lastRev = sessioninfos[session].rev;
|
var lastRev = that.sessioninfos[session].rev;
|
||||||
|
|
||||||
//https://github.com/caolan/async#whilst
|
//https://github.com/caolan/async#whilst
|
||||||
//send them all new changesets
|
//send them all new changesets
|
||||||
|
@ -517,14 +530,14 @@ exports.updatePadClients = function(pad, callback)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
// next if session has not been deleted
|
// next if session has not been deleted
|
||||||
if(sessioninfos[session] == null)
|
if(that.sessioninfos[session] == null)
|
||||||
{
|
{
|
||||||
callback(null);
|
callback(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(author == sessioninfos[session].author)
|
if(author == that.sessioninfos[session].author)
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[session].json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}});
|
that.socketio.sockets.sockets[session].json.send({"type":"COLLABROOM","data":{type:"ACCEPT_COMMIT", newRev:r}});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -534,7 +547,7 @@ exports.updatePadClients = function(pad, callback)
|
||||||
apool: forWire.pool,
|
apool: forWire.pool,
|
||||||
author: author}};
|
author: author}};
|
||||||
|
|
||||||
socketio.sockets.sockets[session].json.send(wireMsg);
|
that.socketio.sockets.sockets[session].json.send(wireMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null);
|
callback(null);
|
||||||
|
@ -543,12 +556,12 @@ exports.updatePadClients = function(pad, callback)
|
||||||
callback
|
callback
|
||||||
);
|
);
|
||||||
|
|
||||||
if(sessioninfos[session] != null)
|
if(that.sessioninfos[session] != null)
|
||||||
{
|
{
|
||||||
sessioninfos[session].rev = pad.getHeadRevisionNumber();
|
that.sessioninfos[session].rev = pad.getHeadRevisionNumber();
|
||||||
}
|
}
|
||||||
},callback);
|
},callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copied from the Etherpad Source Code. Don't know what this methode does excatly...
|
* Copied from the Etherpad Source Code. Don't know what this methode does excatly...
|
||||||
|
@ -598,7 +611,7 @@ function _correctMarkersInPad(atext, apool) {
|
||||||
* @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
|
||||||
*/
|
*/
|
||||||
function handleClientReady(client, message)
|
PadMessageHandler.prototype.handleClientReady = function handleClientReady(client, message)
|
||||||
{
|
{
|
||||||
//check if all ok
|
//check if all ok
|
||||||
if(!message.token)
|
if(!message.token)
|
||||||
|
@ -629,12 +642,13 @@ function handleClientReady(client, message)
|
||||||
var historicalAuthorData = {};
|
var historicalAuthorData = {};
|
||||||
var readOnlyId;
|
var readOnlyId;
|
||||||
var chatMessages;
|
var chatMessages;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
//check permissions
|
//check permissions
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
|
that.securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
@ -658,7 +672,7 @@ function handleClientReady(client, message)
|
||||||
//get colorId
|
//get colorId
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
authorManager.getAuthorColorId(author, function(err, value)
|
that.authorManager.getAuthorColorId(author, function(err, value)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
authorColorId = value;
|
authorColorId = value;
|
||||||
|
@ -668,7 +682,7 @@ function handleClientReady(client, message)
|
||||||
//get author name
|
//get author name
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
authorManager.getAuthorName(author, function(err, value)
|
that.authorManager.getAuthorName(author, function(err, value)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
authorName = value;
|
authorName = value;
|
||||||
|
@ -677,7 +691,7 @@ function handleClientReady(client, message)
|
||||||
},
|
},
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
padManager.getPad(message.padId, function(err, value)
|
that.padManager.getPad(message.padId, function(err, value)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
pad = value;
|
pad = value;
|
||||||
|
@ -686,7 +700,7 @@ function handleClientReady(client, message)
|
||||||
},
|
},
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
readOnlyManager.getReadOnlyId(message.padId, function(err, value)
|
that.readOnlyManager.getReadOnlyId(message.padId, function(err, value)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
readOnlyId = value;
|
readOnlyId = value;
|
||||||
|
@ -706,7 +720,7 @@ function handleClientReady(client, message)
|
||||||
{
|
{
|
||||||
async.forEach(authors, function(authorId, callback)
|
async.forEach(authors, function(authorId, callback)
|
||||||
{
|
{
|
||||||
authorManager.getAuthor(authorId, function(err, author)
|
that.authorManager.getAuthor(authorId, function(err, author)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
delete author.timestamp;
|
delete author.timestamp;
|
||||||
|
@ -732,29 +746,29 @@ function handleClientReady(client, message)
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
//Check if this author is already on the pad, if yes, kick the other sessions!
|
//Check if this author is already on the pad, if yes, kick the other sessions!
|
||||||
if(pad2sessions[message.padId])
|
if(that.pad2sessions[message.padId])
|
||||||
{
|
{
|
||||||
for(var i in pad2sessions[message.padId])
|
for(var i in that.pad2sessions[message.padId])
|
||||||
{
|
{
|
||||||
if(sessioninfos[pad2sessions[message.padId][i]].author == author)
|
if(that.sessioninfos[that.pad2sessions[message.padId][i]].author == author)
|
||||||
{
|
{
|
||||||
socketio.sockets.sockets[pad2sessions[message.padId][i]].json.send({disconnect:"userdup"});
|
that.socketio.sockets.sockets[that.pad2sessions[message.padId][i]].json.send({disconnect:"userdup"});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save in session2pad that this session belonges to this pad
|
//Save in session2pad that this session belonges to this pad
|
||||||
var sessionId=String(client.id);
|
var sessionId=String(client.id);
|
||||||
session2pad[sessionId] = message.padId;
|
that.session2pad[sessionId] = message.padId;
|
||||||
|
|
||||||
//check if there is already a pad2sessions entry, if not, create one
|
//check if there is already a pad2sessions entry, if not, create one
|
||||||
if(!pad2sessions[message.padId])
|
if(!that.pad2sessions[message.padId])
|
||||||
{
|
{
|
||||||
pad2sessions[message.padId] = [];
|
that.pad2sessions[message.padId] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
//Saves in pad2sessions that this session belongs to this pad
|
//Saves in pad2sessions that this session belongs to this pad
|
||||||
pad2sessions[message.padId].push(sessionId);
|
that.pad2sessions[message.padId].push(sessionId);
|
||||||
|
|
||||||
//prepare all values for the wire
|
//prepare all values for the wire
|
||||||
var atext = Changeset.cloneAText(pad.atext);
|
var atext = Changeset.cloneAText(pad.atext);
|
||||||
|
@ -788,7 +802,7 @@ function handleClientReady(client, message)
|
||||||
"initialTitle": "Pad: " + message.padId,
|
"initialTitle": "Pad: " + message.padId,
|
||||||
"opts": {},
|
"opts": {},
|
||||||
"chatHistory": chatMessages,
|
"chatHistory": chatMessages,
|
||||||
"numConnectedUsers": pad2sessions[message.padId].length,
|
"numConnectedUsers": that.pad2sessions[message.padId].length,
|
||||||
"isProPad": false,
|
"isProPad": false,
|
||||||
"readOnlyId": readOnlyId,
|
"readOnlyId": readOnlyId,
|
||||||
"serverTimestamp": new Date().getTime(),
|
"serverTimestamp": new Date().getTime(),
|
||||||
|
@ -798,7 +812,7 @@ function handleClientReady(client, message)
|
||||||
"fullWidth": false,
|
"fullWidth": false,
|
||||||
"hideSidebar": false
|
"hideSidebar": false
|
||||||
},
|
},
|
||||||
"abiwordAvailable": settings.abiwordAvailable(),
|
"abiwordAvailable": that.settings.abiwordAvailable(),
|
||||||
"hooks": {}
|
"hooks": {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,13 +822,13 @@ function handleClientReady(client, message)
|
||||||
clientVars.userName = authorName;
|
clientVars.userName = authorName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(sessioninfos[client.id] !== undefined)
|
if(that.sessioninfos[client.id] !== undefined)
|
||||||
{
|
{
|
||||||
//This is a reconnect, so we don't have to send the client the ClientVars again
|
//This is a reconnect, so we don't have to send the client the ClientVars again
|
||||||
if(message.reconnect == true)
|
if(message.reconnect == true)
|
||||||
{
|
{
|
||||||
//Save the revision in sessioninfos, we take the revision from the info the client send to us
|
//Save the revision in sessioninfos, we take the revision from the info the client send to us
|
||||||
sessioninfos[client.id].rev = message.client_rev;
|
that.sessioninfos[client.id].rev = message.client_rev;
|
||||||
}
|
}
|
||||||
//This is a normal first connect
|
//This is a normal first connect
|
||||||
else
|
else
|
||||||
|
@ -822,11 +836,11 @@ function handleClientReady(client, message)
|
||||||
//Send the clientVars to the Client
|
//Send the clientVars to the Client
|
||||||
client.json.send(clientVars);
|
client.json.send(clientVars);
|
||||||
//Save the revision in sessioninfos
|
//Save the revision in sessioninfos
|
||||||
sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
|
that.sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save the revision and the author id in sessioninfos
|
//Save the revision and the author id in sessioninfos
|
||||||
sessioninfos[client.id].author = author;
|
that.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
|
||||||
|
@ -850,7 +864,7 @@ function handleClientReady(client, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Run trough all sessions of this pad
|
//Run trough all sessions of this pad
|
||||||
async.forEach(pad2sessions[message.padId], function(sessionID, callback)
|
async.forEach(that.pad2sessions[message.padId], function(sessionID, callback)
|
||||||
{
|
{
|
||||||
var sessionAuthorName, sessionAuthorColorId;
|
var sessionAuthorName, sessionAuthorColorId;
|
||||||
|
|
||||||
|
@ -861,21 +875,21 @@ function handleClientReady(client, message)
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
authorManager.getAuthorColorId(sessioninfos[sessionID].author, function(err, value)
|
that.authorManager.getAuthorColorId(that.sessioninfos[sessionID].author, function(err, value)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
sessionAuthorColorId = value;
|
sessionAuthorColorId = value;
|
||||||
callback();
|
callback();
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
function(callback)
|
function(callback)
|
||||||
{
|
{
|
||||||
authorManager.getAuthorName(sessioninfos[sessionID].author, function(err, value)
|
that.authorManager.getAuthorName(that.sessioninfos[sessionID].author, function(err, value)
|
||||||
{
|
{
|
||||||
if(ERR(err, callback)) return;
|
if(ERR(err, callback)) return;
|
||||||
sessionAuthorName = value;
|
sessionAuthorName = value;
|
||||||
callback();
|
callback();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
],callback);
|
],callback);
|
||||||
},
|
},
|
||||||
|
@ -885,7 +899,7 @@ function handleClientReady(client, message)
|
||||||
if(sessionID != client.id)
|
if(sessionID != client.id)
|
||||||
{
|
{
|
||||||
//Send this Session the Notification about the new user
|
//Send this Session the Notification about the new user
|
||||||
socketio.sockets.sockets[sessionID].json.send(messageToTheOtherUsers);
|
that.socketio.sockets.sockets[sessionID].json.send(messageToTheOtherUsers);
|
||||||
|
|
||||||
//Send the new User a Notification about this other user
|
//Send the new User a Notification about this other user
|
||||||
var messageToNotifyTheClientAboutTheOthers = {
|
var messageToNotifyTheClientAboutTheOthers = {
|
||||||
|
@ -897,7 +911,7 @@ function handleClientReady(client, message)
|
||||||
"colorId": sessionAuthorColorId,
|
"colorId": sessionAuthorColorId,
|
||||||
"name": sessionAuthorName,
|
"name": sessionAuthorName,
|
||||||
"userAgent": "Anonymous",
|
"userAgent": "Anonymous",
|
||||||
"userId": sessioninfos[sessionID].author
|
"userId": that.sessioninfos[sessionID].author
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -911,4 +925,4 @@ function handleClientReady(client, message)
|
||||||
{
|
{
|
||||||
ERR(err);
|
ERR(err);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
|
@ -24,36 +24,43 @@ var log4js = require('log4js');
|
||||||
var messageLogger = log4js.getLogger("message");
|
var messageLogger = log4js.getLogger("message");
|
||||||
var securityManager = require("../db/SecurityManager");
|
var securityManager = require("../db/SecurityManager");
|
||||||
|
|
||||||
/**
|
var SocketIORouter = function SocketIORouter(securityManager) {
|
||||||
|
this.securityManager = securityManager;
|
||||||
|
/**
|
||||||
* Saves all components
|
* Saves all components
|
||||||
* key is the component name
|
* key is the component name
|
||||||
* value is the component module
|
* value is the component module
|
||||||
*/
|
*/
|
||||||
var components = {};
|
this.components = {};
|
||||||
|
|
||||||
var socket;
|
this.socket = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* adds a component
|
* adds a component
|
||||||
*/
|
*/
|
||||||
exports.addComponent = function(moduleName, module)
|
};
|
||||||
|
|
||||||
|
exports.SocketIORouter = SocketIORouter;
|
||||||
|
|
||||||
|
SocketIORouter.prototype.addComponent = function(moduleName, module)
|
||||||
{
|
{
|
||||||
//save the component
|
//save the component
|
||||||
components[moduleName] = module;
|
this.components[moduleName] = module;
|
||||||
|
|
||||||
//give the module the socket
|
//give the module the socket
|
||||||
module.setSocketIO(socket);
|
module.setSocketIO(this.socket);
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sets the socket.io and adds event functions for routing
|
* sets the socket.io and adds event functions for routing
|
||||||
*/
|
*/
|
||||||
exports.setSocketIO = function(_socket)
|
SocketIORouter.prototype.setSocketIO = function(_socket)
|
||||||
{
|
{
|
||||||
|
var that = this;
|
||||||
//save this socket internaly
|
//save this socket internaly
|
||||||
socket = _socket;
|
this.socket = _socket;
|
||||||
|
|
||||||
socket.sockets.on('connection', function(client)
|
this.socket.sockets.on('connection', function(client)
|
||||||
{
|
{
|
||||||
var clientAuthorized = false;
|
var clientAuthorized = false;
|
||||||
|
|
||||||
|
@ -63,24 +70,24 @@ exports.setSocketIO = function(_socket)
|
||||||
{
|
{
|
||||||
messageLogger.info("to " + client.id + ": " + stringifyWithoutPassword(message));
|
messageLogger.info("to " + client.id + ": " + stringifyWithoutPassword(message));
|
||||||
client._send(message);
|
client._send(message);
|
||||||
}
|
};
|
||||||
|
|
||||||
//tell all components about this connect
|
//tell all components about this connect
|
||||||
for(var i in components)
|
for(var i in that.components)
|
||||||
{
|
{
|
||||||
components[i].handleConnect(client);
|
that.components[i].handleConnect(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
//try to handle the message of this client
|
//try to handle the message of this client
|
||||||
function handleMessage(message)
|
function handleMessage(message)
|
||||||
{
|
{
|
||||||
if(message.component && components[message.component])
|
if(message.component && that.components[message.component])
|
||||||
{
|
{
|
||||||
//check if component is registered in the components array
|
//check if component is registered in the components array
|
||||||
if(components[message.component])
|
if(that.components[message.component])
|
||||||
{
|
{
|
||||||
messageLogger.info("from " + client.id + ": " + stringifyWithoutPassword(message));
|
messageLogger.info("from " + client.id + ": " + stringifyWithoutPassword(message));
|
||||||
components[message.component].handleMessage(client, message);
|
that.components[message.component].handleMessage(client, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -108,7 +115,7 @@ exports.setSocketIO = function(_socket)
|
||||||
//this message has everything to try an authorization
|
//this message has everything to try an authorization
|
||||||
if(message.padId !== undefined && message.sessionID !== undefined && message.token !== undefined && message.password !== undefined)
|
if(message.padId !== undefined && message.sessionID !== undefined && message.token !== undefined && message.password !== undefined)
|
||||||
{
|
{
|
||||||
securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
|
that.securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
|
||||||
{
|
{
|
||||||
ERR(err);
|
ERR(err);
|
||||||
|
|
||||||
|
@ -137,27 +144,30 @@ exports.setSocketIO = function(_socket)
|
||||||
client.on('disconnect', function()
|
client.on('disconnect', function()
|
||||||
{
|
{
|
||||||
//tell all components about this disconnect
|
//tell all components about this disconnect
|
||||||
for(var i in components)
|
for(var i in that.components)
|
||||||
{
|
{
|
||||||
components[i].handleDisconnect(client);
|
that.components[i].handleDisconnect(client);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
//returns a stringified representation of a message, removes the password
|
//returns a stringified representation of a message, removes the password
|
||||||
//this ensures there are no passwords in the log
|
//this ensures there are no passwords in the log
|
||||||
function stringifyWithoutPassword(message)
|
function stringifyWithoutPassword(message)
|
||||||
{
|
{
|
||||||
var newMessage = {};
|
var newMessage = {};
|
||||||
|
//FIXME LOL
|
||||||
for(var i in message)
|
for(var i in message)
|
||||||
{
|
{
|
||||||
if(i == "password" && message[i] != null)
|
if(i == "password" && message[i] != null) {
|
||||||
newMessage["password"] = "xxx";
|
newMessage["password"] = "xxx";
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
newMessage[i]=message[i];
|
newMessage[i]=message[i];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return JSON.stringify(newMessage);
|
return JSON.stringify(newMessage);
|
||||||
}
|
}
|
||||||
|
|
48
node/routes/api.js
Normal file
48
node/routes/api.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
var formidable = require('formidable');
|
||||||
|
var log4js = require('log4js');
|
||||||
|
|
||||||
|
module.exports = function(app)
|
||||||
|
{
|
||||||
|
var apiLogger = log4js.getLogger("API");
|
||||||
|
|
||||||
|
//This is for making an api call, collecting all post information and passing it to the apiHandler
|
||||||
|
var apiCaller = function(req, res, fields)
|
||||||
|
{
|
||||||
|
res.header("Content-Type", "application/json; charset=utf-8");
|
||||||
|
|
||||||
|
apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields));
|
||||||
|
|
||||||
|
//wrap the send function so we can log the response
|
||||||
|
res._send = res.send;
|
||||||
|
res.send = function(response)
|
||||||
|
{
|
||||||
|
response = JSON.stringify(response);
|
||||||
|
apiLogger.info("RESPONSE, " + req.params.func + ", " + response);
|
||||||
|
|
||||||
|
//is this a jsonp call, if yes, add the function call
|
||||||
|
if(req.query.jsonp)
|
||||||
|
response = req.query.jsonp + "(" + response + ")";
|
||||||
|
|
||||||
|
res._send(response);
|
||||||
|
};
|
||||||
|
|
||||||
|
//call the api handler
|
||||||
|
app.apiHandler.handle(req.params.func, fields, req, res);
|
||||||
|
};
|
||||||
|
|
||||||
|
//This is a api GET call, collect all post informations and pass it to the apiHandler
|
||||||
|
app.get('/api/1/:func', function(req, res)
|
||||||
|
{
|
||||||
|
apiCaller(req, res, req.query);
|
||||||
|
});
|
||||||
|
|
||||||
|
//This is a api POST call, collect all post informations and pass it to the apiHandler
|
||||||
|
app.post('/api/1/:func', function(req, res)
|
||||||
|
{
|
||||||
|
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
||||||
|
{
|
||||||
|
apiCaller(req, res, fields);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
25
node/routes/debug.js
Normal file
25
node/routes/debug.js
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
var formidable = require('formidable');
|
||||||
|
|
||||||
|
module.exports = function(app)
|
||||||
|
{
|
||||||
|
//The Etherpad client side sends information about how a disconnect happen
|
||||||
|
app.post('/ep/pad/connection-diagnostic-info', function(req, res)
|
||||||
|
{
|
||||||
|
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
||||||
|
{
|
||||||
|
console.log("DIAGNOSTIC-INFO: " + fields.diagnosticInfo);
|
||||||
|
res.end("OK");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//The Etherpad client side sends information about client side javscript errors
|
||||||
|
app.post('/jserror', function(req, res)
|
||||||
|
{
|
||||||
|
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
||||||
|
{
|
||||||
|
console.error("CLIENT SIDE JAVASCRIPT ERROR: " + fields.errorInfo);
|
||||||
|
res.end("OK");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
34
node/routes/export.js
Normal file
34
node/routes/export.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
var ERR = require("async-stacktrace");
|
||||||
|
|
||||||
|
module.exports = function(app)
|
||||||
|
{
|
||||||
|
var hasPadAccess = require('./preconditions').hasPadAccess(app);
|
||||||
|
|
||||||
|
//serve timeslider.html under /p/$padname/timeslider
|
||||||
|
app.get('/p/:pad/:rev?/export/:type', function(req, res, next)
|
||||||
|
{
|
||||||
|
var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
|
||||||
|
//send a 404 if we don't support this filetype
|
||||||
|
if(types.indexOf(req.params.type) == -1)
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if abiword is disabled, and this is a format we only support with abiword, output a message
|
||||||
|
if(app.settings.abiword == null &&
|
||||||
|
["odt", "pdf", "doc"].indexOf(req.params.type) !== -1)
|
||||||
|
{
|
||||||
|
res.send("Abiword is not enabled at this Etherpad Lite instance. Set the path to Abiword in settings.json to enable this feature");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.header("Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
|
hasPadAccess(req, res, function()
|
||||||
|
{
|
||||||
|
app.exportHandler.doExport(req, res, req.params.pad, req.params.type);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
23
node/routes/import.js
Normal file
23
node/routes/import.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
var ERR = require("async-stacktrace");
|
||||||
|
|
||||||
|
module.exports = function(app)
|
||||||
|
{
|
||||||
|
|
||||||
|
var hasPadAccess = require('./preconditions').hasPadAccess(app);
|
||||||
|
|
||||||
|
//handle import requests
|
||||||
|
app.post('/p/:pad/import', function(req, res, next)
|
||||||
|
{
|
||||||
|
//if abiword is disabled, skip handling this request
|
||||||
|
if(app.settings.abiword == null)
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPadAccess(req, res, function()
|
||||||
|
{
|
||||||
|
app.importHandler.doImport(req, res, req.params.pad);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
57
node/routes/preconditions.js
Normal file
57
node/routes/preconditions.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
var ERR = require('async-stacktrace');
|
||||||
|
|
||||||
|
module.exports = function(app)
|
||||||
|
{
|
||||||
|
//redirects browser to the pad's sanitized url if needed. otherwise, renders the html
|
||||||
|
app.param('pad', function (req, res, next, padId) {
|
||||||
|
//ensure the padname is valid and the url doesn't end with a /
|
||||||
|
if(!app.padManager.isValidPadId(padId) || /\/$/.test(req.url))
|
||||||
|
{
|
||||||
|
res.send('Such a padname is forbidden', 404);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
app.padManager.sanitizePadId(padId, function(sanitizedPadId) {
|
||||||
|
//the pad id was sanitized, so we redirect to the sanitized version
|
||||||
|
if(sanitizedPadId != padId)
|
||||||
|
{
|
||||||
|
var real_path = req.path.replace(/^\/p\/[^\/]+/, '/p/' + sanitizedPadId);
|
||||||
|
res.header('Location', real_path);
|
||||||
|
res.send('You should be redirected to <a href="' + real_path + '">' + real_path + '</a>', 302);
|
||||||
|
}
|
||||||
|
//the pad id was fine, so just render it
|
||||||
|
else
|
||||||
|
{
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.hasPadAccess = function(app)
|
||||||
|
{
|
||||||
|
|
||||||
|
//checks for padAccess
|
||||||
|
var hasPadAccess = function hasPadAccess(req, res, callback)
|
||||||
|
{
|
||||||
|
app.securityManager.checkAccess(req.params.pad, req.cookies.sessionid, req.cookies.token, req.cookies.password, function(err, accessObj)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
//there is access, continue
|
||||||
|
if(accessObj.accessStatus == "grant")
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
//no access
|
||||||
|
else
|
||||||
|
{
|
||||||
|
res.send("403 - Can't touch this", 403);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return hasPadAccess;
|
||||||
|
|
||||||
|
};
|
65
node/routes/readonly.js
Normal file
65
node/routes/readonly.js
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
var async = require('async');
|
||||||
|
var ERR = require('async-stacktrace');
|
||||||
|
|
||||||
|
module.exports = function(app)
|
||||||
|
{
|
||||||
|
|
||||||
|
var hasPadAccess = require('./preconditions').hasPadAccess(app);
|
||||||
|
|
||||||
|
//serve read only pad
|
||||||
|
app.get('/ro/:id', function(req, res)
|
||||||
|
{
|
||||||
|
var html;
|
||||||
|
var padId;
|
||||||
|
var pad;
|
||||||
|
|
||||||
|
async.series([
|
||||||
|
//translate the read only pad to a padId
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
app.readOnlyManager.getPadId(req.params.id, function(err, _padId)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
|
||||||
|
padId = _padId;
|
||||||
|
|
||||||
|
//we need that to tell hasPadAcess about the pad
|
||||||
|
req.params.pad = padId;
|
||||||
|
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//render the html document
|
||||||
|
function(callback)
|
||||||
|
{
|
||||||
|
//return if the there is no padId
|
||||||
|
if(padId == null)
|
||||||
|
{
|
||||||
|
callback("notfound");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPadAccess(req, res, function()
|
||||||
|
{
|
||||||
|
//render the html document
|
||||||
|
app.exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html)
|
||||||
|
{
|
||||||
|
if(ERR(err, callback)) return;
|
||||||
|
html = _html;
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
], function(err)
|
||||||
|
{
|
||||||
|
//throw any unexpected error
|
||||||
|
if(err && err != "notfound")
|
||||||
|
ERR(err);
|
||||||
|
|
||||||
|
if(err == "notfound")
|
||||||
|
res.send('404 - Not Found', 404);
|
||||||
|
else
|
||||||
|
res.send(html);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
55
node/routes/static.js
Normal file
55
node/routes/static.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
var path = require('path');
|
||||||
|
|
||||||
|
module.exports = function(app)
|
||||||
|
{
|
||||||
|
|
||||||
|
app.get('/static/*', function(req, res)
|
||||||
|
{
|
||||||
|
var filePath = path.normalize(__dirname + "/../.." +
|
||||||
|
req.url.replace(/\.\./g, '').split("?")[0]);
|
||||||
|
res.sendfile(filePath, { maxAge: app.maxAge });
|
||||||
|
});
|
||||||
|
|
||||||
|
//serve pad.html under /p
|
||||||
|
app.get('/p/:pad', function(req, res, next)
|
||||||
|
{
|
||||||
|
var filePath = path.normalize(__dirname + "/../../static/pad.html");
|
||||||
|
res.sendfile(filePath, { maxAge: app.maxAge });
|
||||||
|
});
|
||||||
|
|
||||||
|
//serve timeslider.html under /p/$padname/timeslider
|
||||||
|
app.get('/p/:pad/timeslider', function(req, res, next)
|
||||||
|
{
|
||||||
|
var filePath = path.normalize(__dirname + "/../../static/timeslider.html");
|
||||||
|
res.sendfile(filePath, { maxAge: app.maxAge });
|
||||||
|
});
|
||||||
|
|
||||||
|
//serve index.html under /
|
||||||
|
app.get('/', function(req, res)
|
||||||
|
{
|
||||||
|
var filePath = path.normalize(__dirname + "/../../static/index.html");
|
||||||
|
res.sendfile(filePath, { maxAge: app.maxAge });
|
||||||
|
});
|
||||||
|
|
||||||
|
//serve robots.txt
|
||||||
|
app.get('/robots.txt', function(req, res)
|
||||||
|
{
|
||||||
|
var filePath = path.normalize(__dirname + "/../../static/robots.txt");
|
||||||
|
res.sendfile(filePath, { maxAge: app.maxAge });
|
||||||
|
});
|
||||||
|
|
||||||
|
//serve favicon.ico
|
||||||
|
app.get('/favicon.ico', function(req, res)
|
||||||
|
{
|
||||||
|
var filePath = path.normalize(__dirname + "/../../static/custom/favicon.ico");
|
||||||
|
res.sendfile(filePath, { maxAge: app.maxAge }, function(err)
|
||||||
|
{
|
||||||
|
//there is no custom favicon, send the default favicon
|
||||||
|
if(err)
|
||||||
|
{
|
||||||
|
filePath = path.normalize(__dirname + "/../../static/favicon.ico");
|
||||||
|
res.sendfile(filePath, { maxAge: app.maxAge });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
348
node/server.js
348
node/server.js
|
@ -20,32 +20,22 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var ERR = require("async-stacktrace");
|
|
||||||
var log4js = require('log4js');
|
var log4js = require('log4js');
|
||||||
var os = require("os");
|
var os = require("os");
|
||||||
var socketio = require('socket.io');
|
var socketio = require('socket.io');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var settings = require('./utils/Settings');
|
var parseSettings = require('./utils/Settings').parseSettings;
|
||||||
var db = require('./db/DB');
|
var db = require('./db/DB');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var minify = require('./utils/Minify');
|
var minify = require('./utils/Minify');
|
||||||
var formidable = require('formidable');
|
|
||||||
var apiHandler;
|
|
||||||
var exportHandler;
|
|
||||||
var importHandler;
|
|
||||||
var exporthtml;
|
|
||||||
var readOnlyManager;
|
|
||||||
var padManager;
|
|
||||||
var securityManager;
|
|
||||||
var socketIORouter;
|
|
||||||
|
|
||||||
//try to get the git version
|
//try to get the git version
|
||||||
var version = "";
|
var version = "";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var rootPath = path.normalize(__dirname + "/../")
|
var rootPath = path.normalize(__dirname + "/../");
|
||||||
var ref = fs.readFileSync(rootPath + ".git/HEAD", "utf-8");
|
var ref = fs.readFileSync(rootPath + ".git/HEAD", "utf-8");
|
||||||
var refPath = rootPath + ".git/" + ref.substring(5, ref.indexOf("\n"));
|
var refPath = rootPath + ".git/" + ref.substring(5, ref.indexOf("\n"));
|
||||||
version = fs.readFileSync(refPath, "utf-8");
|
version = fs.readFileSync(refPath, "utf-8");
|
||||||
|
@ -54,72 +44,76 @@ try
|
||||||
}
|
}
|
||||||
catch(e)
|
catch(e)
|
||||||
{
|
{
|
||||||
console.warn("Can't get git version for server header\n" + e.message)
|
console.warn("Can't get git version for server header\n" + e.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Report bugs at https://github.com/Pita/etherpad-lite/issues")
|
console.log("Report bugs at https://github.com/Pita/etherpad-lite/issues");
|
||||||
|
|
||||||
var serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
|
var serverName = "Etherpad-Lite " + version + " (http://j.mp/ep-lite)";
|
||||||
|
|
||||||
|
var settings = parseSettings(__dirname + '/../settings.json');
|
||||||
|
|
||||||
//cache 6 hours
|
//cache 6 hours
|
||||||
exports.maxAge = 1000*60*60*6;
|
exports.maxAge = 1000*60*60*6;
|
||||||
|
|
||||||
//set loglevel
|
//set loglevel
|
||||||
log4js.setGlobalLogLevel(settings.loglevel);
|
log4js.setGlobalLogLevel(settings.logLevel);
|
||||||
|
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
//initalize the database
|
//initalize the database
|
||||||
function (callback)
|
function (callback)
|
||||||
{
|
{
|
||||||
db.init(callback);
|
db.init(settings, callback);
|
||||||
},
|
},
|
||||||
|
//TOD rename dbInstance
|
||||||
//initalize the http server
|
//initalize the http server
|
||||||
function (callback)
|
function (dbInstance, callback) {
|
||||||
{
|
|
||||||
//create server
|
//create server
|
||||||
var app = express.createServer();
|
var app = express.createServer();
|
||||||
|
|
||||||
|
app.maxAge = exports.maxAge;
|
||||||
|
app.settings = settings;
|
||||||
|
|
||||||
app.use(function (req, res, next) {
|
app.use(function (req, res, next) {
|
||||||
res.header("Server", serverName);
|
res.header("Server", serverName);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//preconditions i.e. sanitize urls
|
||||||
|
require('./routes/preconditions')(app);
|
||||||
|
|
||||||
//redirects browser to the pad's sanitized url if needed. otherwise, renders the html
|
var PadManager = require('./db/PadManager').PadManager;
|
||||||
app.param('pad', function (req, res, next, padId) {
|
|
||||||
//ensure the padname is valid and the url doesn't end with a /
|
var ReadOnlyManager = require('./db/ReadOnlyManager').ReadOnlyManager;
|
||||||
if(!padManager.isValidPadId(padId) || /\/$/.test(req.url))
|
|
||||||
{
|
var SecurityManager = require('./db/SecurityManager').SecurityManager;
|
||||||
res.send('Such a padname is forbidden', 404);
|
|
||||||
}
|
var AuthorManager = require('./db/AuthorManager').AuthorManager;
|
||||||
else
|
|
||||||
{
|
var GroupManager = require('./db/GroupManager').GroupManager;
|
||||||
padManager.sanitizePadId(padId, function(sanitizedPadId) {
|
|
||||||
//the pad id was sanitized, so we redirect to the sanitized version
|
var SessionManager = require('./db/SessionManager').SessionManager;
|
||||||
if(sanitizedPadId != padId)
|
|
||||||
{
|
|
||||||
var real_path = req.path.replace(/^\/p\/[^\/]+/, '/p/' + sanitizedPadId);
|
|
||||||
res.header('Location', real_path);
|
|
||||||
res.send('You should be redirected to <a href="' + real_path + '">' + real_path + '</a>', 302);
|
|
||||||
}
|
|
||||||
//the pad id was fine, so just render it
|
|
||||||
else
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//load modules that needs a initalized db
|
//load modules that needs a initalized db
|
||||||
readOnlyManager = require("./db/ReadOnlyManager");
|
app.readOnlyManager = new ReadOnlyManager(dbInstance);
|
||||||
exporthtml = require("./utils/ExportHtml");
|
|
||||||
exportHandler = require('./handler/ExportHandler');
|
app.authorManager = new AuthorManager(dbInstance);
|
||||||
importHandler = require('./handler/ImportHandler');
|
|
||||||
apiHandler = require('./handler/APIHandler');
|
app.padManager = new PadManager(settings, dbInstance, app.authorManager, app.readOnlyManager);
|
||||||
padManager = require('./db/PadManager');
|
|
||||||
securityManager = require('./db/SecurityManager');
|
app.groupManager = new GroupManager(dbInstance, app.padManager, null);
|
||||||
socketIORouter = require("./handler/SocketIORouter");
|
|
||||||
|
app.sessionManager = new SessionManager(dbInstance, app.groupManager, app.authorManager);
|
||||||
|
|
||||||
|
app.securityManager = new SecurityManager(settings, dbInstance, app.authorManager, app.padManager, app.sessionManager);
|
||||||
|
|
||||||
|
app.exporthtml = require("./utils/ExportHtml");
|
||||||
|
app.exportHandler = require('./handler/ExportHandler');
|
||||||
|
app.importHandler = require('./handler/ImportHandler');
|
||||||
|
|
||||||
|
//app.apiHandler = require('./handler/APIHandler');
|
||||||
|
|
||||||
|
var SocketIORouter = require("./handler/SocketIORouter").SocketIORouter;
|
||||||
|
|
||||||
//install logging
|
//install logging
|
||||||
var httpLogger = log4js.getLogger("http");
|
var httpLogger = log4js.getLogger("http");
|
||||||
|
@ -130,8 +124,9 @@ async.waterfall([
|
||||||
|
|
||||||
// If the log level specified in the config file is WARN or ERROR the application server never starts listening to requests as reported in issue #158.
|
// If the log level specified in the config file is WARN or ERROR the application server never starts listening to requests as reported in issue #158.
|
||||||
// Not installing the log4js connect logger when the log level has a higher severity than INFO since it would not log at that level anyway.
|
// Not installing the log4js connect logger when the log level has a higher severity than INFO since it would not log at that level anyway.
|
||||||
if (!(settings.loglevel === "WARN" || settings.loglevel == "ERROR"))
|
if (!(settings.loglevel === "WARN" || settings.loglevel == "ERROR")) {
|
||||||
app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'}));
|
app.use(log4js.connectLogger(httpLogger, { level: log4js.levels.INFO, format: ':status, :method :url'}));
|
||||||
|
}
|
||||||
app.use(express.cookieParser());
|
app.use(express.cookieParser());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -147,36 +142,10 @@ async.waterfall([
|
||||||
res.write(minify.requireDefinition());
|
res.write(minify.requireDefinition());
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
app.get('/static/*', function(req, res)
|
|
||||||
{
|
|
||||||
var filePath = path.normalize(__dirname + "/.." +
|
|
||||||
req.url.replace(/\.\./g, '').split("?")[0]);
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve minified files
|
//serve minified files
|
||||||
app.get('/minified/:filename', minify.minifyJS);
|
app.get('/minified/:filename', minify.minifyJS);
|
||||||
|
|
||||||
//checks for padAccess
|
|
||||||
function hasPadAccess(req, res, callback)
|
|
||||||
{
|
|
||||||
securityManager.checkAccess(req.params.pad, req.cookies.sessionid, req.cookies.token, req.cookies.password, function(err, accessObj)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
|
|
||||||
//there is access, continue
|
|
||||||
if(accessObj.accessStatus == "grant")
|
|
||||||
{
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
//no access
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res.send("403 - Can't touch this", 403);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//checks for basic http auth
|
//checks for basic http auth
|
||||||
function basic_auth (req, res, next) {
|
function basic_auth (req, res, next) {
|
||||||
if (req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
|
if (req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
|
||||||
|
@ -197,210 +166,17 @@ async.waterfall([
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//serve read only pad
|
require('./routes/readonly')(app);
|
||||||
app.get('/ro/:id', function(req, res)
|
|
||||||
{
|
|
||||||
var html;
|
|
||||||
var padId;
|
|
||||||
var pad;
|
|
||||||
|
|
||||||
async.series([
|
require('./routes/import')(app);
|
||||||
//translate the read only pad to a padId
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
readOnlyManager.getPadId(req.params.id, function(err, _padId)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
|
|
||||||
padId = _padId;
|
require('./routes/export')(app);
|
||||||
|
|
||||||
//we need that to tell hasPadAcess about the pad
|
//require('./routes/api')(app);
|
||||||
req.params.pad = padId;
|
|
||||||
|
|
||||||
callback();
|
require('./routes/debug')(app);
|
||||||
});
|
|
||||||
},
|
|
||||||
//render the html document
|
|
||||||
function(callback)
|
|
||||||
{
|
|
||||||
//return if the there is no padId
|
|
||||||
if(padId == null)
|
|
||||||
{
|
|
||||||
callback("notfound");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPadAccess(req, res, function()
|
require('./routes/static')(app);
|
||||||
{
|
|
||||||
//render the html document
|
|
||||||
exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html)
|
|
||||||
{
|
|
||||||
if(ERR(err, callback)) return;
|
|
||||||
html = _html;
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
], function(err)
|
|
||||||
{
|
|
||||||
//throw any unexpected error
|
|
||||||
if(err && err != "notfound")
|
|
||||||
ERR(err);
|
|
||||||
|
|
||||||
if(err == "notfound")
|
|
||||||
res.send('404 - Not Found', 404);
|
|
||||||
else
|
|
||||||
res.send(html);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve pad.html under /p
|
|
||||||
app.get('/p/:pad', function(req, res, next)
|
|
||||||
{
|
|
||||||
var filePath = path.normalize(__dirname + "/../static/pad.html");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve timeslider.html under /p/$padname/timeslider
|
|
||||||
app.get('/p/:pad/timeslider', function(req, res, next)
|
|
||||||
{
|
|
||||||
var filePath = path.normalize(__dirname + "/../static/timeslider.html");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve timeslider.html under /p/$padname/timeslider
|
|
||||||
app.get('/p/:pad/:rev?/export/:type', function(req, res, next)
|
|
||||||
{
|
|
||||||
var types = ["pdf", "doc", "txt", "html", "odt", "dokuwiki"];
|
|
||||||
//send a 404 if we don't support this filetype
|
|
||||||
if(types.indexOf(req.params.type) == -1)
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//if abiword is disabled, and this is a format we only support with abiword, output a message
|
|
||||||
if(settings.abiword == null &&
|
|
||||||
["odt", "pdf", "doc"].indexOf(req.params.type) !== -1)
|
|
||||||
{
|
|
||||||
res.send("Abiword is not enabled at this Etherpad Lite instance. Set the path to Abiword in settings.json to enable this feature");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.header("Access-Control-Allow-Origin", "*");
|
|
||||||
|
|
||||||
hasPadAccess(req, res, function()
|
|
||||||
{
|
|
||||||
exportHandler.doExport(req, res, req.params.pad, req.params.type);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//handle import requests
|
|
||||||
app.post('/p/:pad/import', function(req, res, next)
|
|
||||||
{
|
|
||||||
//if abiword is disabled, skip handling this request
|
|
||||||
if(settings.abiword == null)
|
|
||||||
{
|
|
||||||
next();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPadAccess(req, res, function()
|
|
||||||
{
|
|
||||||
importHandler.doImport(req, res, req.params.pad);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
var apiLogger = log4js.getLogger("API");
|
|
||||||
|
|
||||||
//This is for making an api call, collecting all post information and passing it to the apiHandler
|
|
||||||
var apiCaller = function(req, res, fields)
|
|
||||||
{
|
|
||||||
res.header("Content-Type", "application/json; charset=utf-8");
|
|
||||||
|
|
||||||
apiLogger.info("REQUEST, " + req.params.func + ", " + JSON.stringify(fields));
|
|
||||||
|
|
||||||
//wrap the send function so we can log the response
|
|
||||||
res._send = res.send;
|
|
||||||
res.send = function(response)
|
|
||||||
{
|
|
||||||
response = JSON.stringify(response);
|
|
||||||
apiLogger.info("RESPONSE, " + req.params.func + ", " + response);
|
|
||||||
|
|
||||||
//is this a jsonp call, if yes, add the function call
|
|
||||||
if(req.query.jsonp)
|
|
||||||
response = req.query.jsonp + "(" + response + ")";
|
|
||||||
|
|
||||||
res._send(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
//call the api handler
|
|
||||||
apiHandler.handle(req.params.func, fields, req, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
//This is a api GET call, collect all post informations and pass it to the apiHandler
|
|
||||||
app.get('/api/1/:func', function(req, res)
|
|
||||||
{
|
|
||||||
apiCaller(req, res, req.query)
|
|
||||||
});
|
|
||||||
|
|
||||||
//This is a api POST call, collect all post informations and pass it to the apiHandler
|
|
||||||
app.post('/api/1/:func', function(req, res)
|
|
||||||
{
|
|
||||||
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
|
||||||
{
|
|
||||||
apiCaller(req, res, fields)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//The Etherpad client side sends information about how a disconnect happen
|
|
||||||
app.post('/ep/pad/connection-diagnostic-info', function(req, res)
|
|
||||||
{
|
|
||||||
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
|
||||||
{
|
|
||||||
console.log("DIAGNOSTIC-INFO: " + fields.diagnosticInfo);
|
|
||||||
res.end("OK");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//The Etherpad client side sends information about client side javscript errors
|
|
||||||
app.post('/jserror', function(req, res)
|
|
||||||
{
|
|
||||||
new formidable.IncomingForm().parse(req, function(err, fields, files)
|
|
||||||
{
|
|
||||||
console.error("CLIENT SIDE JAVASCRIPT ERROR: " + fields.errorInfo);
|
|
||||||
res.end("OK");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve index.html under /
|
|
||||||
app.get('/', function(req, res)
|
|
||||||
{
|
|
||||||
var filePath = path.normalize(__dirname + "/../static/index.html");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve robots.txt
|
|
||||||
app.get('/robots.txt', function(req, res)
|
|
||||||
{
|
|
||||||
var filePath = path.normalize(__dirname + "/../static/robots.txt");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
|
||||||
});
|
|
||||||
|
|
||||||
//serve favicon.ico
|
|
||||||
app.get('/favicon.ico', function(req, res)
|
|
||||||
{
|
|
||||||
var filePath = path.normalize(__dirname + "/../static/custom/favicon.ico");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge }, function(err)
|
|
||||||
{
|
|
||||||
//there is no custom favicon, send the default favicon
|
|
||||||
if(err)
|
|
||||||
{
|
|
||||||
filePath = path.normalize(__dirname + "/../static/favicon.ico");
|
|
||||||
res.sendfile(filePath, { maxAge: exports.maxAge });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//let the server listen
|
//let the server listen
|
||||||
app.listen(settings.port, settings.ip);
|
app.listen(settings.port, settings.ip);
|
||||||
|
@ -428,7 +204,7 @@ async.waterfall([
|
||||||
app.close();
|
app.close();
|
||||||
|
|
||||||
//do the db shutdown
|
//do the db shutdown
|
||||||
db.db.doShutdown(function()
|
dbInstance.doShutdown(function()
|
||||||
{
|
{
|
||||||
console.log("db sucessfully closed.");
|
console.log("db sucessfully closed.");
|
||||||
|
|
||||||
|
@ -481,13 +257,19 @@ async.waterfall([
|
||||||
if(settings.minify)
|
if(settings.minify)
|
||||||
io.enable('browser client minification');
|
io.enable('browser client minification');
|
||||||
|
|
||||||
var padMessageHandler = require("./handler/PadMessageHandler");
|
var PadMessageHandler = require("./handler/PadMessageHandler").PadMessageHandler;
|
||||||
var timesliderMessageHandler = require("./handler/TimesliderMessageHandler");
|
|
||||||
|
app.padMessageHandler = new PadMessageHandler(app.settings, app.padManager, app.authorManager, app.readOnlyManager, app.securityManager);
|
||||||
|
|
||||||
|
//var timesliderMessageHandler = require("./handler/TimesliderMessageHandler");
|
||||||
|
|
||||||
|
|
||||||
//Initalize the Socket.IO Router
|
//Initalize the Socket.IO Router
|
||||||
socketIORouter.setSocketIO(io);
|
//
|
||||||
socketIORouter.addComponent("pad", padMessageHandler);
|
app.socketIORouter = new SocketIORouter(app.securityManager);
|
||||||
socketIORouter.addComponent("timeslider", timesliderMessageHandler);
|
app.socketIORouter.setSocketIO(io);
|
||||||
|
app.socketIORouter.addComponent("pad", app.padMessageHandler);
|
||||||
|
//socketIORouter.addComponent("timeslider", timesliderMessageHandler);
|
||||||
|
|
||||||
callback(null);
|
callback(null);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,109 +23,103 @@ var fs = require("fs");
|
||||||
var os = require("os");
|
var os = require("os");
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
|
var defaults = {};
|
||||||
/**
|
/**
|
||||||
* The IP ep-lite should listen to
|
* The IP ep-lite should listen to
|
||||||
*/
|
*/
|
||||||
exports.ip = "0.0.0.0";
|
defaults.ip = "0.0.0.0";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Port ep-lite should listen to
|
* The Port ep-lite should listen to
|
||||||
*/
|
*/
|
||||||
exports.port = 9001;
|
defaults.port = 9001;
|
||||||
/*
|
/*
|
||||||
* The Type of the database
|
* The Type of the database
|
||||||
*/
|
*/
|
||||||
exports.dbType = "dirty";
|
defaults.dbType = "dirty";
|
||||||
/**
|
/**
|
||||||
* This setting is passed with dbType to ueberDB to set up the database
|
* This setting is passed with dbType to ueberDB to set up the database
|
||||||
*/
|
*/
|
||||||
exports.dbSettings = { "filename" : "../var/dirty.db" };
|
defaults.dbSettings = { "filename" : "../var/dirty.db" };
|
||||||
/**
|
/**
|
||||||
* The default Text of a new pad
|
* The default Text of a new pad
|
||||||
*/
|
*/
|
||||||
exports.defaultPadText = "Welcome to Etherpad Lite!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad Lite on Github: http:\/\/j.mp/ep-lite\n";
|
defaults.defaultPadText = "Welcome to Etherpad Lite!\n\nThis pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!\n\nEtherpad Lite on Github: http:\/\/j.mp/ep-lite\n";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A flag that requires any user to have a valid session (via the api) before accessing a pad
|
* A flag that requires any user to have a valid session (via the api) before accessing a pad
|
||||||
*/
|
*/
|
||||||
exports.requireSession = false;
|
defaults.requireSession = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A flag that prevents users from creating new pads
|
* A flag that prevents users from creating new pads
|
||||||
*/
|
*/
|
||||||
exports.editOnly = false;
|
defaults.editOnly = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A flag that shows if minification is enabled or not
|
* A flag that shows if minification is enabled or not
|
||||||
*/
|
*/
|
||||||
exports.minify = true;
|
defaults.minify = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The path of the abiword executable
|
* The path of the abiword executable
|
||||||
*/
|
*/
|
||||||
exports.abiword = null;
|
defaults.abiword = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The log level of log4js
|
* The log level of log4js
|
||||||
*/
|
*/
|
||||||
exports.loglevel = "INFO";
|
defaults.logLevel = "INFO";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Http basic auth, with "user:password" format
|
* Http basic auth, with "user:password" format
|
||||||
*/
|
*/
|
||||||
exports.httpAuth = null;
|
defaults.httpAuth = null;
|
||||||
|
|
||||||
//checks if abiword is avaiable
|
|
||||||
exports.abiwordAvailable = function()
|
|
||||||
{
|
var Settings = function(settings) {
|
||||||
if(exports.abiword != null)
|
|
||||||
{
|
this.ip = settings.ip || defaults.ip;
|
||||||
|
this.port = settings.port || defaults.port;
|
||||||
|
this.dbType = settings.dbType || defaults.dbType;
|
||||||
|
this.dbSettings = settings.dbSettings || defaults.dbSettings;
|
||||||
|
this.defaultPadText = settings.defaultPadText || defaults.defaultPadText;
|
||||||
|
this.requireSessions = settings.requireSessions || defaults.requireSessions;
|
||||||
|
this.editOnly = settings.editOnly || defaults.editOnly;
|
||||||
|
this.minify = settings.minify || defaults.minify;
|
||||||
|
this.abiword = settings.abiword || defaults.abiword;
|
||||||
|
this.logLevel = settings.logLevel || defaults.logLevel;
|
||||||
|
this.httpAuth = settings.httpAuth || defaults.httpAuth;
|
||||||
|
};
|
||||||
|
|
||||||
|
//TODO this is shit
|
||||||
|
Settings.prototype.abiwordAvailable = function abiwordAvailable() {
|
||||||
|
if(this.abiword != null) {
|
||||||
return os.type().indexOf("Windows") != -1 ? "withoutPDF" : "yes";
|
return os.type().indexOf("Windows") != -1 ? "withoutPDF" : "yes";
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
{
|
|
||||||
return "no";
|
return "no";
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
exports.Settings = Settings;
|
||||||
//read the settings sync
|
//read the settings sync
|
||||||
var settingsPath = path.normalize(__dirname + "/../../");
|
|
||||||
var settingsStr = fs.readFileSync(settingsPath + "settings.json").toString();
|
|
||||||
|
|
||||||
//remove all comments
|
exports.parseSettings = function parseSettings(path) {
|
||||||
settingsStr = settingsStr.replace(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/gm,"").replace(/#.*/g,"").replace(/\/\/.*/g,"");
|
|
||||||
|
|
||||||
//try to parse the settings
|
var settingsStr = fs.readFileSync(path).toString();
|
||||||
var settings;
|
settingsStr = settingsStr.replace(/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+/gm,"").replace(/#.*/g,"").replace(/\/\/.*/g,"");
|
||||||
try
|
var pojo;
|
||||||
{
|
//try to parse the settings
|
||||||
settings = JSON.parse(settingsStr);
|
try {
|
||||||
}
|
pojo = JSON.parse(settingsStr);
|
||||||
catch(e)
|
}
|
||||||
{
|
catch(e) {
|
||||||
console.error("There is a syntax error in your settings.json file");
|
console.log(e);
|
||||||
console.error(e.message);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
return new Settings(pojo);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
//loop trough the settings
|
|
||||||
for(var i in settings)
|
|
||||||
{
|
|
||||||
//test if the setting start with a low character
|
|
||||||
if(i.charAt(0).search("[a-z]") !== 0)
|
|
||||||
{
|
|
||||||
console.warn("Settings should start with a low character: '" + i + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
//we know this setting, so we overwrite it
|
|
||||||
if(exports[i] !== undefined)
|
|
||||||
{
|
|
||||||
exports[i] = settings[i];
|
|
||||||
}
|
|
||||||
//this setting is unkown, output a warning and throw it away
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console.warn("Unkown Setting: '" + i + "'");
|
|
||||||
console.warn("This setting doesn't exist or it was removed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue