merge with upstream. Fixed confict in pad.html

This commit is contained in:
Jean-Tiare Le Bigot 2012-01-08 19:32:06 +01:00
parent 3248ca4cbb
commit 254046454d
91 changed files with 1674 additions and 1363 deletions

View file

@ -18,6 +18,8 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var customError = require("../utils/customError");
var padManager = require("./PadManager");
var padMessageHandler = require("../handler/PadMessageHandler");
var readOnlyManager = require("./ReadOnlyManager");
@ -87,7 +89,7 @@ exports.getText = function(padID, rev, callback)
}
else
{
callback({stop: "rev is not a number"});
callback(new customError("rev is not a number", "apierror"));
return;
}
}
@ -95,25 +97,21 @@ exports.getText = function(padID, rev, callback)
//ensure this is not a negativ number
if(rev !== undefined && rev < 0)
{
callback({stop: "rev is a negativ number"});
callback(new customError("rev is a negativ number","apierror"));
return;
}
//ensure this is not a float value
if(rev !== undefined && !is_int(rev))
{
callback({stop: "rev is a float value"});
callback(new customError("rev is a float value","apierror"));
return;
}
//get the pad
getPadSafe(padID, true, function(err, pad)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//the client asked for a special revision
if(rev !== undefined)
@ -121,19 +119,18 @@ exports.getText = function(padID, rev, callback)
//check if this is a valid revision
if(rev > pad.getHeadRevisionNumber())
{
callback({stop: "rev is higher than the head revision of the pad"});
callback(new customError("rev is higher than the head revision of the pad","apierror"));
return;
}
//get the text of this revision
pad.getInternalRevisionAText(rev, function(err, atext)
{
if(!err)
{
data = {text: atext.text};
}
if(ERR(err, callback)) return;
callback(err, data);
data = {text: atext.text};
callback(null, data);
})
}
//the client wants the latest text, lets return it to him
@ -158,11 +155,7 @@ exports.setText = function(padID, text, callback)
//get the pad
getPadSafe(padID, true, function(err, pad)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//set the text
pad.setText(text);
@ -196,30 +189,26 @@ exports.getHTML = function(padID, rev, callback)
}
else
{
callback({stop: "rev is not a number"});
callback(new customError("rev is not a number","apierror"));
return;
}
}
if(rev !== undefined && rev < 0)
{
callback({stop: "rev is a negative number"});
callback(new customError("rev is a negative number","apierror"));
return;
}
if(rev !== undefined && !is_int(rev))
{
callback({stop: "rev is a float value"});
callback(new customError("rev is a float value","apierror"));
return;
}
getPadSafe(padID, true, function(err, pad)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//the client asked for a special revision
if(rev !== undefined)
@ -227,18 +216,16 @@ exports.getHTML = function(padID, rev, callback)
//check if this is a valid revision
if(rev > pad.getHeadRevisionNumber())
{
callback({stop: "rev is higher than the head revision of the pad"});
callback(new customError("rev is higher than the head revision of the pad","apierror"));
return;
}
//get the html of this revision
exportHtml.getPadHTML(pad, rev, function(err, html)
{
if(!err)
{
data = {html: html};
}
callback(err, data);
if(ERR(err, callback)) return;
data = {html: html};
callback(null, data);
});
}
//the client wants the latest text, lets return it to him
@ -246,11 +233,11 @@ exports.getHTML = function(padID, rev, callback)
{
exportHtml.getPadHTML(pad, undefined, function (err, html)
{
if(!err)
{
data = {html: html};
}
callback(err, data);
if(ERR(err, callback)) return;
data = {html: html};
callback(null, data);
});
}
});
@ -261,11 +248,7 @@ exports.setHTML = function(padID, html, callback)
//get the pad
getPadSafe(padID, true, function(err, pad)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
// add a new changeset with the new html to the pad
importHtml.setPadHTML(pad, cleanText(html));
@ -293,11 +276,7 @@ exports.getRevisionsCount = function(padID, callback)
//get the pad
getPadSafe(padID, true, function(err, pad)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
callback(null, {revisions: pad.getHeadRevisionNumber()});
});
@ -314,16 +293,17 @@ Example returns:
exports.createPad = function(padID, text, callback)
{
//ensure there is no $ in the padID
if(padID.indexOf("$") != -1)
if(padID && padID.indexOf("$") != -1)
{
callback({stop: "createPad can't create group pads"});
callback(new customError("createPad can't create group pads","apierror"));
return;
}
//create pad
getPadSafe(padID, false, text, function(err)
{
callback(err);
if(ERR(err, callback)) return;
callback();
});
}
@ -339,11 +319,7 @@ exports.deletePad = function(padID, callback)
{
getPadSafe(padID, true, function(err, pad)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
pad.remove(callback);
});
@ -362,16 +338,13 @@ exports.getReadOnlyID = function(padID, callback)
//we don't need the pad object, but this function does all the security stuff for us
getPadSafe(padID, true, function(err)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//get the readonlyId
readOnlyManager.getReadOnlyId(padID, function(err, readOnlyId)
{
callback(err, {readOnlyID: readOnlyId});
if(ERR(err, callback)) return;
callback(null, {readOnlyID: readOnlyId});
});
});
}
@ -387,20 +360,16 @@ Example returns:
exports.setPublicStatus = function(padID, publicStatus, callback)
{
//ensure this is a group pad
if(padID.indexOf("$") == -1)
if(padID && padID.indexOf("$") == -1)
{
callback({stop: "You can only get/set the publicStatus of pads that belong to a group"});
callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror"));
return;
}
//get the pad
getPadSafe(padID, true, function(err, pad)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//convert string to boolean
if(typeof publicStatus == "string")
@ -424,20 +393,16 @@ Example returns:
exports.getPublicStatus = function(padID, callback)
{
//ensure this is a group pad
if(padID.indexOf("$") == -1)
if(padID && padID.indexOf("$") == -1)
{
callback({stop: "You can only get/set the publicStatus of pads that belong to a group"});
callback(new customError("You can only get/set the publicStatus of pads that belong to a group","apierror"));
return;
}
//get the pad
getPadSafe(padID, true, function(err, pad)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
callback(null, {publicStatus: pad.getPublicStatus()});
});
@ -454,20 +419,16 @@ Example returns:
exports.setPassword = function(padID, password, callback)
{
//ensure this is a group pad
if(padID.indexOf("$") == -1)
if(padID && padID.indexOf("$") == -1)
{
callback({stop: "You can only get/set the password of pads that belong to a group"});
callback(new customError("You can only get/set the password of pads that belong to a group","apierror"));
return;
}
//get the pad
getPadSafe(padID, true, function(err, pad)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//set the password
pad.setPassword(password);
@ -487,20 +448,16 @@ Example returns:
exports.isPasswordProtected = function(padID, callback)
{
//ensure this is a group pad
if(padID.indexOf("$") == -1)
if(padID && padID.indexOf("$") == -1)
{
callback({stop: "You can only get/set the password of pads that belong to a group"});
callback(new customError("You can only get/set the password of pads that belong to a group","apierror"));
return;
}
//get the pad
getPadSafe(padID, true, function(err, pad)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
callback(null, {isPasswordProtected: pad.isPasswordProtected()});
});
@ -528,34 +485,31 @@ function getPadSafe(padID, shouldExist, text, callback)
//check if padID is a string
if(typeof padID != "string")
{
callback({stop: "padID is not a string"});
callback(new customError("padID is not a string","apierror"));
return;
}
//check if the padID maches the requirements
if(!padManager.isValidPadId(padID))
{
callback({stop: "padID did not match requirements"});
callback(new customError("padID did not match requirements","apierror"));
return;
}
//check if the pad exists
padManager.doesPadExists(padID, function(err, exists)
{
//error
if(err)
{
callback(err);
}
if(ERR(err, callback)) return;
//does not exist, but should
else if(exists == false && shouldExist == true)
if(exists == false && shouldExist == true)
{
callback({stop: "padID does not exist"});
callback(new customError("padID does not exist","apierror"));
}
//does exists, but shouldn't
else if(exists == true && shouldExist == false)
{
callback({stop: "padID does already exist"});
callback(new customError("padID does already exist","apierror"));
}
//pad exists, let's get it
else

View file

@ -18,6 +18,7 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var db = require("./DB").db;
var async = require("async");
@ -29,7 +30,8 @@ exports.doesAuthorExists = function (authorID, callback)
//check if the database entry of this author exists
db.get("globalAuthor:" + authorID, function (err, author)
{
callback(err, author != null);
if(ERR(err, callback)) return;
callback(null, author != null);
});
}
@ -42,8 +44,9 @@ exports.getAuthor4Token = function (token, callback)
{
mapAuthorWithDBKey("token2author", token, function(err, author)
{
if(ERR(err, callback)) return;
//return only the sub value authorID
callback(err, author ? author.authorID : author);
callback(null, author ? author.authorID : author);
});
}
@ -56,12 +59,7 @@ exports.createAuthorIfNotExistsFor = function (authorMapper, name, callback)
{
mapAuthorWithDBKey("mapper2author", authorMapper, function(err, author)
{
//error?
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//set the name of this author
if(name)
@ -84,24 +82,14 @@ function mapAuthorWithDBKey (mapperkey, mapper, callback)
//try to map to an author
db.get(mapperkey + ":" + mapper, function (err, author)
{
//error?
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//there is no author with this mapper, so create one
if(author == null)
{
exports.createAuthor(null, function(err, author)
{
//error?
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//create the token2author relation
db.set(mapperkey + ":" + mapper, author.authorID);

View file

@ -18,6 +18,8 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var customError = require("../utils/customError");
var db = require("./DB").db;
var async = require("async");
var padManager = require("./PadManager");
@ -34,15 +36,12 @@ exports.deleteGroup = function(groupID, callback)
//try to get the group entry
db.get("group:" + groupID, function (err, _group)
{
//error
if(err)
{
callback(err);
}
if(ERR(err, callback)) return;
//group does not exist
else if(_group == null)
if(_group == null)
{
callback({stop: "groupID does not exist"});
callback(new customError("groupID does not exist","apierror"));
}
//group exists, everything is fine
else
@ -67,7 +66,7 @@ exports.deleteGroup = function(groupID, callback)
{
padManager.getPad(padID, function(err, pad)
{
if(err) {callback(err); return}
if(ERR(err, callback)) return;
pad.remove(callback);
});
@ -79,7 +78,7 @@ exports.deleteGroup = function(groupID, callback)
//try to get the group entry
db.get("group2sessions:" + groupID, function (err, group2sessions)
{
if(err) {callback(err); return}
if(ERR(err, callback)) return;
//skip if there is no group2sessions entry
if(group2sessions == null) {callback(); return}
@ -107,7 +106,8 @@ exports.deleteGroup = function(groupID, callback)
}
], function(err)
{
callback(err);
if(ERR(err, callback)) return;
callback();
});
}
@ -116,7 +116,8 @@ exports.doesGroupExist = function(groupID, callback)
//try to get the group entry
db.get("group:" + groupID, function (err, group)
{
callback(err, group != null);
if(ERR(err, callback)) return;
callback(null, group != null);
});
}
@ -135,30 +136,21 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback)
//ensure mapper is optional
if(typeof groupMapper != "string")
{
callback({stop: "groupMapper is no string"});
callback(new customError("groupMapper is no string","apierror"));
return;
}
//try to get a group for this mapper
db.get("mapper2group:"+groupMapper, function(err, groupID)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//there is no group for this mapper, let's create a group
if(groupID == null)
{
exports.createGroup(function(err, responseObj)
{
//check for errors
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//create the mapper entry for this group
db.set("mapper2group:"+groupMapper, responseObj.groupID);
@ -169,7 +161,8 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback)
//there is a group for this mapper, let's return it
else
{
callback(err, {groupID: groupID});
if(ERR(err, callback)) return;
callback(null, {groupID: groupID});
}
});
}
@ -185,15 +178,12 @@ exports.createGroupPad = function(groupID, padName, text, callback)
{
exports.doesGroupExist(groupID, function(err, exists)
{
//error
if(err)
{
callback(err);
}
if(ERR(err, callback)) return;
//group does not exist
else if(exists == false)
if(exists == false)
{
callback({stop: "groupID does not exist"});
callback(new customError("groupID does not exist","apierror"));
}
//group exists, everything is fine
else
@ -207,15 +197,12 @@ exports.createGroupPad = function(groupID, padName, text, callback)
{
padManager.doesPadExists(padID, function(err, exists)
{
//error
if(err)
{
callback(err);
}
if(ERR(err, callback)) return;
//pad exists already
else if(exists == true)
if(exists == true)
{
callback({stop: "padName does already exist"});
callback(new customError("padName does already exist","apierror"));
}
//pad does not exist, everything is fine
else
@ -229,7 +216,8 @@ exports.createGroupPad = function(groupID, padName, text, callback)
{
padManager.getPad(padID, text, function(err)
{
callback(err);
if(ERR(err, callback)) return;
callback();
});
},
//create an entry in the group for this pad
@ -240,7 +228,8 @@ exports.createGroupPad = function(groupID, padName, text, callback)
}
], function(err)
{
callback(err, {padID: padID});
if(ERR(err, callback)) return;
callback(null, {padID: padID});
});
}
@ -248,22 +237,20 @@ exports.listPads = function(groupID, callback)
{
exports.doesGroupExist(groupID, function(err, exists)
{
//error
if(err)
{
callback(err);
}
if(ERR(err, callback)) return;
//group does not exist
else if(exists == false)
if(exists == false)
{
callback({stop: "groupID does not exist"});
callback(new customError("groupID does not exist","apierror"));
}
//group exists, let's get the pads
else
{
db.getSub("group:" + groupID, ["pads"], function(err, pads)
{
callback(err, {padIDs: pads});
if(ERR(err, callback)) return;
callback(null, {padIDs: pads});
});
}
});

View file

@ -4,6 +4,7 @@
require('joose');
var ERR = require("async-stacktrace");
var Changeset = require("../utils/Changeset");
var AttributePoolFactory = require("../utils/AttributePoolFactory");
var db = require("./DB").db;
@ -164,8 +165,9 @@ Class('Pad', {
{
db.getSub("pad:"+_this.id+":revs:"+keyRev, ["meta", "atext"], function(err, _atext)
{
if(ERR(err, callback)) return;
atext = Changeset.cloneAText(_atext);
callback(err);
callback();
});
},
//get all needed changesets
@ -175,8 +177,9 @@ Class('Pad', {
{
_this.getRevisionChangeset(item, function(err, changeset)
{
if(ERR(err, callback)) return;
changesets[item] = changeset;
callback(err);
callback();
});
}, callback);
}
@ -199,7 +202,8 @@ Class('Pad', {
}
], function(err)
{
callback(err, atext);
if(ERR(err, callback)) return;
callback(null, atext);
});
},
@ -247,8 +251,9 @@ Class('Pad', {
{
db.get("pad:"+_this.id+":chat:"+entryNum, function(err, _entry)
{
if(ERR(err, callback)) return;
entry = _entry;
callback(err);
callback();
});
},
//add the authorName
@ -264,13 +269,15 @@ Class('Pad', {
//get the authorName
authorManager.getAuthorName(entry.userId, function(err, authorName)
{
if(ERR(err, callback)) return;
entry.userName = authorName;
callback(err);
callback();
});
}
], function(err)
{
callback(err, entry);
if(ERR(err, callback)) return;
callback(null, entry);
});
},
@ -311,11 +318,14 @@ Class('Pad', {
{
_this.getChatMessage(entryObject.entryNum, function(err, entry)
{
if(ERR(err, callback)) return;
entries[entryObject.order] = entry;
callback(err);
callback();
});
}, function(err)
{
if(ERR(err, callback)) return;
//sort out broken chat entries
//it looks like in happend in the past that the chat head was
//incremented, but the chat message wasn't added
@ -328,7 +338,7 @@ Class('Pad', {
console.warn("WARNING: Found broken chat entry in pad " + _this.id);
}
callback(err, cleanedEntries);
callback(null, cleanedEntries);
});
},
@ -345,11 +355,7 @@ Class('Pad', {
//try to load the pad
db.get("pad:"+this.id, function(err, value)
{
if(err)
{
callback(err, null);
return;
}
if(ERR(err, callback)) return;
//if this pad exists, load it
if(value != null)
@ -410,7 +416,7 @@ Class('Pad', {
db.get("group:" + groupID, function (err, group)
{
if(err) {callback(err); return}
if(ERR(err, callback)) return;
//remove the pad entry
delete group.pads[padID];
@ -432,7 +438,7 @@ Class('Pad', {
{
readOnlyManager.getReadOnlyId(padID, function(err, readonlyID)
{
if(err) {callback(err); return}
if(ERR(err, callback)) return;
db.remove("pad2readonly:" + padID);
db.remove("readonly2pad:" + readonlyID);
@ -475,7 +481,8 @@ Class('Pad', {
}
], function(err)
{
callback(err);
if(ERR(err, callback)) return;
callback();
})
},
//set in db

View file

@ -18,6 +18,8 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var customError = require("../utils/customError");
require("../db/Pad");
var db = require("./DB").db;
@ -30,12 +32,20 @@ var db = require("./DB").db;
* If this is needed in other places, it would be wise to make this a prototype
* that's defined somewhere more sensible.
*/
globalPads = {
var globalPads = {
get: function (name) { return this[':'+name]; },
set: function (name, value) { this[':'+name] = value; },
remove: function (name) { delete this[':'+name]; }
};
/**
* 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.
*/
var padIdTransforms = [
[/\s+/g, '_']
];
/**
* Returns a Pad Object with the callback
* @param id A String with the id of the pad
@ -46,7 +56,7 @@ exports.getPad = function(id, text, callback)
//check if this is a valid padId
if(!exports.isValidPadId(id))
{
callback({stop: id + " is not a valid padId"});
callback(new customError(id + " is not a valid padId","apierror"));
return;
}
@ -63,14 +73,14 @@ exports.getPad = function(id, text, callback)
//check if text is a string
if(typeof text != "string")
{
callback({stop: "text is not a string"});
callback(new customError("text is not a string","apierror"));
return;
}
//check if text is less than 100k chars
if(text.length > 100000)
{
callback({stop: "text must be less than 100k chars"});
callback(new customError("text must be less than 100k chars","apierror"));
return;
}
}
@ -90,15 +100,10 @@ exports.getPad = function(id, text, callback)
//initalize the pad
pad.init(text, function(err)
{
if(err)
{
callback(err, null);
}
else
{
globalPads.set(id, pad);
callback(null, pad);
}
if(ERR(err, callback)) return;
globalPads.set(id, pad);
callback(null, pad);
});
}
}
@ -108,10 +113,44 @@ exports.doesPadExists = function(padId, callback)
{
db.get("pad:"+padId, function(err, value)
{
callback(err, value != null);
if(ERR(err, callback)) return;
callback(null, value != null);
});
}
//returns a sanitized padId, respecting legacy pad id formats
exports.sanitizePadId = function(padId, callback) {
var transform_index = arguments[2] || 0;
//we're out of possible transformations, so just return it
if(transform_index >= padIdTransforms.length)
{
callback(padId);
}
//check if padId exists
else
{
exports.doesPadExists(padId, function(junk, exists)
{
if(exists)
{
callback(padId);
}
else
{
//get the next transformation *that's different*
var transformedPadId = padId;
while(transformedPadId == padId && transform_index < padIdTransforms.length)
{
transformedPadId = padId.replace(padIdTransforms[transform_index][0], padIdTransforms[transform_index][1]);
transform_index += 1;
}
//check the next transform
exports.sanitizePadId(transformedPadId, callback, transform_index);
}
});
}
}
exports.isValidPadId = function(padId)
{
return /^(g.[a-zA-Z0-9]{16}\$)?[^$]{1,50}$/.test(padId);

View file

@ -18,6 +18,7 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var db = require("./DB").db;
var async = require("async");
@ -55,8 +56,9 @@ exports.getReadOnlyId = function (padId, callback)
}
], function(err)
{
if(ERR(err, callback)) return;
//return the results
callback(err, readOnlyId);
callback(null, readOnlyId);
})
}

View file

@ -17,7 +17,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var db = require("./DB").db;
var async = require("async");
var authorManager = require("./AuthorManager");
@ -56,6 +57,8 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
//get author for this token
authorManager.getAuthor4Token(token, function(err, author)
{
if(ERR(err, callback)) return;
// assume user has access
statusObject = {accessStatus: "grant", authorID: author};
// user can't create pads
@ -64,17 +67,19 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
// check if pad exists
padManager.doesPadExists(padID, function(err, exists)
{
if(ERR(err, callback)) return;
// pad doesn't exist - user can't have access
if(!exists) statusObject.accessStatus = "deny";
// grant or deny access, with author of token
callback(err, statusObject);
callback(null, statusObject);
});
}
// user may create new pads - no need to check anything
else
{
// grant access, with author of token
callback(err, statusObject);
callback(null, statusObject);
}
})
@ -102,8 +107,9 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
{
padManager.doesPadExists(padID, function(err, exists)
{
if(ERR(err, callback)) return;
padExists = exists;
callback(err);
callback();
});
},
//get informations about this session
@ -112,13 +118,13 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
sessionManager.getSessionInfo(sessionID, function(err, sessionInfo)
{
//skip session validation if the session doesn't exists
if(err && err.stop == "sessionID does not exist")
if(err && err.message == "sessionID does not exist")
{
callback();
return;
}
if(err) {callback(err); return}
if(ERR(err, callback)) return;
var now = Math.floor(new Date().getTime()/1000);
@ -139,8 +145,9 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
//get author for this token
authorManager.getAuthor4Token(token, function(err, author)
{
if(ERR(err, callback)) return;
tokenAuthor = author;
callback(err);
callback();
});
}
], callback);
@ -157,7 +164,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
padManager.getPad(padID, function(err, pad)
{
if(err) {callback(err); return}
if(ERR(err, callback)) return;
//is it a public pad?
isPublic = pad.getPublicStatus();
@ -265,6 +272,7 @@ exports.checkAccess = function (padID, sessionID, token, password, callback)
}
], function(err)
{
callback(err, statusObject);
if(ERR(err, callback)) return;
callback(null, statusObject);
});
}

View file

@ -18,6 +18,8 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var customError = require("../utils/customError");
var db = require("./DB").db;
var async = require("async");
var groupMangager = require("./GroupManager");
@ -28,7 +30,8 @@ exports.doesSessionExist = function(sessionID, callback)
//check if the database entry of this session exists
db.get("session:" + sessionID, function (err, session)
{
callback(err, session != null);
if(ERR(err, callback)) return;
callback(null, session != null);
});
}
@ -45,15 +48,12 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
{
groupMangager.doesGroupExist(groupID, function(err, exists)
{
//error
if(err)
{
callback(err);
}
if(ERR(err, callback)) return;
//group does not exist
else if(exists == false)
if(exists == false)
{
callback({stop: "groupID does not exist"});
callback(new customError("groupID does not exist","apierror"));
}
//everything is fine, continue
else
@ -67,15 +67,12 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
{
authorMangager.doesAuthorExists(authorID, function(err, exists)
{
//error
if(err)
{
callback(err);
}
if(ERR(err, callback)) return;
//author does not exist
else if(exists == false)
if(exists == false)
{
callback({stop: "authorID does not exist"});
callback(new customError("authorID does not exist","apierror"));
}
//everything is fine, continue
else
@ -97,7 +94,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
}
else
{
callback({stop: "validUntil is not a number"});
callback(new customError("validUntil is not a number","apierror"));
return;
}
}
@ -105,21 +102,21 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
//ensure this is not a negativ number
if(validUntil < 0)
{
callback({stop: "validUntil is a negativ number"});
callback(new customError("validUntil is a negativ number","apierror"));
return;
}
//ensure this is not a float value
if(!is_int(validUntil))
{
callback({stop: "validUntil is a float value"});
callback(new customError("validUntil is a float value","apierror"));
return;
}
//check if validUntil is in the future
if(Math.floor(new Date().getTime()/1000) > validUntil)
{
callback({stop: "validUntil is in the past"});
callback(new customError("validUntil is in the past","apierror"));
return;
}
@ -137,12 +134,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
//get the entry
db.get("group2sessions:" + groupID, function(err, group2sessions)
{
//did a error happen?
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//the entry doesn't exist so far, let's create it
if(group2sessions == null)
@ -165,12 +157,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
//get the entry
db.get("author2sessions:" + authorID, function(err, author2sessions)
{
//did a error happen?
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
//the entry doesn't exist so far, let's create it
if(author2sessions == null)
@ -189,8 +176,10 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
}
], function(err)
{
if(ERR(err, callback)) return;
//return error and sessionID
callback(err, {sessionID: sessionID});
callback(null, {sessionID: sessionID});
})
}
@ -199,15 +188,12 @@ exports.getSessionInfo = function(sessionID, callback)
//check if the database entry of this session exists
db.get("session:" + sessionID, function (err, session)
{
//error
if(err)
{
callback(err);
}
if(ERR(err, callback)) return;
//session does not exists
else if(session == null)
if(session == null)
{
callback({stop: "sessionID does not exist"})
callback(new customError("sessionID does not exist","apierror"))
}
//everything is fine, return the sessioninfos
else
@ -231,15 +217,12 @@ exports.deleteSession = function(sessionID, callback)
//get the session entry
db.get("session:" + sessionID, function (err, session)
{
//error
if(err)
{
callback(err);
}
if(ERR(err, callback)) return;
//session does not exists
else if(session == null)
if(session == null)
{
callback({stop: "sessionID does not exist"})
callback(new customError("sessionID does not exist","apierror"))
}
//everything is fine, return the sessioninfos
else
@ -256,8 +239,9 @@ exports.deleteSession = function(sessionID, callback)
{
db.get("group2sessions:" + groupID, function (err, _group2sessions)
{
if(ERR(err, callback)) return;
group2sessions = _group2sessions;
callback(err);
callback();
});
},
//get the author2sessions entry
@ -265,8 +249,9 @@ exports.deleteSession = function(sessionID, callback)
{
db.get("author2sessions:" + authorID, function (err, _author2sessions)
{
if(ERR(err, callback)) return;
author2sessions = _author2sessions;
callback(err);
callback();
});
},
//remove the values from the database
@ -287,7 +272,8 @@ exports.deleteSession = function(sessionID, callback)
}
], function(err)
{
callback(err);
if(ERR(err, callback)) return;
callback();
})
}
@ -295,15 +281,12 @@ exports.listSessionsOfGroup = function(groupID, callback)
{
groupMangager.doesGroupExist(groupID, function(err, exists)
{
//error
if(err)
{
callback(err);
}
if(ERR(err, callback)) return;
//group does not exist
else if(exists == false)
if(exists == false)
{
callback({stop: "groupID does not exist"});
callback(new customError("groupID does not exist","apierror"));
}
//everything is fine, continue
else
@ -317,15 +300,12 @@ exports.listSessionsOfAuthor = function(authorID, callback)
{
authorMangager.doesAuthorExists(authorID, function(err, exists)
{
//error
if(err)
{
callback(err);
}
if(ERR(err, callback)) return;
//group does not exist
else if(exists == false)
if(exists == false)
{
callback({stop: "authorID does not exist"});
callback(new customError("authorID does not exist","apierror"));
}
//everything is fine, continue
else
@ -346,8 +326,9 @@ function listSessionsWithDBKey (dbkey, callback)
//get the group2sessions entry
db.get(dbkey, function(err, sessionObject)
{
if(ERR(err, callback)) return;
sessions = sessionObject ? sessionObject.sessionIDs : null;
callback(err);
callback();
});
},
function(callback)
@ -364,14 +345,16 @@ function listSessionsWithDBKey (dbkey, callback)
{
exports.getSessionInfo(sessionID, function(err, sessionInfo)
{
if(ERR(err, callback)) return;
sessions[sessionID] = sessionInfo;
callback(err);
callback();
});
}, callback);
}
], function(err)
{
callback(err, sessions);
if(ERR(err, callback)) return;
callback(null, sessions);
});
}

View file

@ -20,8 +20,8 @@
* limitations under the License.
*/
var Changeset = require('./Changeset');
var AttributePoolFactory = require("./AttributePoolFactory");
var Changeset = require('./utils/Changeset');
var AttributePoolFactory = require("./utils/AttributePoolFactory");
function random() {
this.nextInt = function (maxValue) {

View file

@ -18,8 +18,10 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var fs = require("fs");
var api = require("../db/API");
var padManager = require("../db/PadManager");
//ensure we have an apikey
var apikey = null;
@ -94,7 +96,33 @@ exports.handle = function(functionName, fields, req, res)
res.send({code: 3, message: "no such function", data: null});
return;
}
//sanitize any pad id's before continuing
if(fields["padID"])
{
padManager.sanitizePadId(fields["padID"], function(padId)
{
fields["padID"] = padId;
callAPI(functionName, fields, req, res);
});
}
else if(fields["padName"])
{
padManager.sanitizePadId(fields["padName"], function(padId)
{
fields["padName"] = padId;
callAPI(functionName, fields, req, res);
});
}
else
{
callAPI(functionName, fields, req, res);
}
}
//calls the api function
function callAPI(functionName, fields, req, res)
{
//put the function parameters in an array
var functionParams = [];
for(var i=0;i<functions[functionName].length;i++)
@ -114,15 +142,15 @@ exports.handle = function(functionName, fields, req, res)
res.send({code: 0, message: "ok", data: data});
}
// parameters were wrong and the api stopped execution, pass the error
else if(err.stop)
else if(err.name == "apierror")
{
res.send({code: 1, message: err.stop, data: null});
res.send({code: 1, message: err.message, data: null});
}
//an unkown error happend
else
{
res.send({code: 2, message: "internal error", data: null});
throw (err);
ERR(err);
}
});

View file

@ -18,7 +18,9 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var exporthtml = require("../utils/ExportHtml");
var exportdokuwiki = require("../utils/ExportDokuWiki");
var padManager = require("../db/PadManager");
var async = require("async");
var fs = require("fs");
@ -50,10 +52,37 @@ exports.doExport = function(req, res, padId, type)
{
padManager.getPad(padId, function(err, pad)
{
if(err)
throw err;
res.send(pad.text());
ERR(err);
if(req.params.rev){
pad.getInternalRevisionAText(req.params.rev, function(junk, text)
{
res.send(text.text ? text.text : null);
});
}
else
{
res.send(pad.text());
}
});
}
else if(type == 'dokuwiki')
{
var randNum;
var srcFile, destFile;
async.series([
//render the dokuwiki document
function(callback)
{
exportdokuwiki.getPadDokuWikiDocument(padId, req.params.rev, function(err, dokuwiki)
{
res.send(dokuwiki);
callback("stop");
});
},
], function(err)
{
if(err && err != "stop") throw err;
});
}
else
@ -66,10 +95,11 @@ exports.doExport = function(req, res, padId, type)
//render the html document
function(callback)
{
exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html)
exporthtml.getPadHTMLDocument(padId, req.params.rev, false, function(err, _html)
{
if(ERR(err, callback)) return;
html = _html;
callback(err);
callback();
});
},
//decide what to do with the html export
@ -113,19 +143,23 @@ exports.doExport = function(req, res, padId, type)
function(callback)
{
//100ms delay to accomidate for slow windows fs
if(os.type().indexOf("Windows") > -1)
{
setTimeout(function()
{
fs.unlink(destFile, callback);
}, 100);
}
if(os.type().indexOf("Windows") > -1)
{
setTimeout(function()
{
fs.unlink(destFile, callback);
}, 100);
}
else
{
fs.unlink(destFile, callback);
}
}
], callback);
}
], function(err)
{
if(err && err != "stop") throw err;
if(err && err != "stop") ERR(err);
})
}
};

View file

@ -18,6 +18,7 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var padManager = require("../db/PadManager");
var padMessageHandler = require("./PadMessageHandler");
var async = require("async");
@ -122,8 +123,9 @@ exports.doImport = function(req, res, padId)
{
padManager.getPad(padId, function(err, _pad)
{
if(ERR(err, callback)) return;
pad = _pad;
callback(err);
callback();
});
},
@ -132,17 +134,22 @@ exports.doImport = function(req, res, padId)
{
fs.readFile(destFile, "utf8", function(err, _text)
{
if(ERR(err, callback)) return;
text = _text;
//node on windows has a delay on releasing of the file lock.
//We add a 100ms delay to work around this
if(os.type().indexOf("Windows") > -1)
{
if(os.type().indexOf("Windows") > -1)
{
setTimeout(function()
{
callback(err);
callback();
}, 100);
}
}
else
{
callback();
}
});
},
@ -176,7 +183,7 @@ exports.doImport = function(req, res, padId)
return;
}
if(err) throw err;
ERR(err);
//close the connection
res.send("ok");

View file

@ -18,6 +18,7 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var async = require("async");
var padManager = require("../db/PadManager");
var Changeset = require("../utils/Changeset");
@ -27,7 +28,6 @@ var readOnlyManager = require("../db/ReadOnlyManager");
var settings = require('../utils/Settings');
var securityManager = require("../db/SecurityManager");
var log4js = require('log4js');
var os = require("os");
var messageLogger = log4js.getLogger("message");
/**
@ -107,7 +107,7 @@ exports.handleDisconnect = function(client)
//get the author color out of the db
authorManager.getAuthorColorId(author, function(err, color)
{
if(err) throw err;
ERR(err);
//prepare the notification for the other users on the pad, that this user left
var messageToTheOtherUsers = {
@ -218,16 +218,18 @@ function handleChatMessage(client, message)
{
padManager.getPad(padId, function(err, _pad)
{
if(ERR(err, callback)) return;
pad = _pad;
callback(err);
callback();
});
},
function(callback)
{
authorManager.getAuthorName(userId, function(err, _userName)
{
if(ERR(err, callback)) return;
userName = _userName;
callback(err);
callback();
});
},
//save the chat message and broadcast it
@ -257,7 +259,7 @@ function handleChatMessage(client, message)
}
], function(err)
{
if(err) throw err;
ERR(err);
});
}
@ -375,8 +377,9 @@ function handleUserChanges(client, message)
{
padManager.getPad(session2pad[client.id], function(err, value)
{
if(ERR(err, callback)) return;
pad = value;
callback(err);
callback();
});
},
//create the changeset
@ -422,16 +425,10 @@ function handleUserChanges(client, message)
pad.getRevisionChangeset(r, function(err, c)
{
if(err)
{
callback(err);
return;
}
else
{
changeset = Changeset.follow(c, changeset, false, apool);
callback(null);
}
if(ERR(err, callback)) return;
changeset = Changeset.follow(c, changeset, false, apool);
callback(null);
});
},
//use the callback of the series function
@ -469,7 +466,7 @@ function handleUserChanges(client, message)
}
], function(err)
{
if(err) throw err;
ERR(err);
});
}
@ -502,25 +499,23 @@ exports.updatePadClients = function(pad, callback)
{
pad.getRevisionAuthor(r, function(err, value)
{
if(ERR(err, callback)) return;
author = value;
callback(err);
callback();
});
},
function (callback)
{
pad.getRevisionChangeset(r, function(err, value)
{
if(ERR(err, callback)) return;
revChangeset = value;
callback(err);
callback();
});
}
], function(err)
{
if(err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
if(author == sessioninfos[session].author)
{
@ -633,7 +628,7 @@ function handleClientReady(client, message)
{
securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
{
if(err) {callback(err); return}
if(ERR(err, callback)) return;
//access was granted
if(statusObject.accessStatus == "grant")
@ -657,8 +652,9 @@ function handleClientReady(client, message)
{
authorManager.getAuthorColorId(author, function(err, value)
{
if(ERR(err, callback)) return;
authorColorId = value;
callback(err);
callback();
});
},
//get author name
@ -666,24 +662,27 @@ function handleClientReady(client, message)
{
authorManager.getAuthorName(author, function(err, value)
{
if(ERR(err, callback)) return;
authorName = value;
callback(err);
callback();
});
},
function(callback)
{
padManager.getPad(message.padId, function(err, value)
{
if(ERR(err, callback)) return;
pad = value;
callback(err);
callback();
});
},
function(callback)
{
readOnlyManager.getReadOnlyId(message.padId, function(err, value)
{
if(ERR(err, callback)) return;
readOnlyId = value;
callback(err);
callback();
});
}
], callback);
@ -701,9 +700,10 @@ function handleClientReady(client, message)
{
authorManager.getAuthor(authorId, function(err, author)
{
if(ERR(err, callback)) return;
delete author.timestamp;
historicalAuthorData[authorId] = author;
callback(err);
callback();
});
}, callback);
},
@ -712,8 +712,9 @@ function handleClientReady(client, message)
{
pad.getLastChatMessages(100, function(err, _chatMessages)
{
if(ERR(err, callback)) return;
chatMessages = _chatMessages;
callback(err);
callback();
});
}
], callback);
@ -753,13 +754,6 @@ function handleClientReady(client, message)
var apool = attribsForWire.pool.toJsonable();
atext.attribs = attribsForWire.translated;
//check if abiword is avaiable
var abiwordAvailable = settings.abiword != null ? "yes" : "no";
if(settings.abiword != null && os.type().indexOf("Windows") != -1)
{
abiwordAvailable = "withoutPDF";
}
var clientVars = {
"accountPrivs": {
"maxRevisions": 100
@ -796,7 +790,7 @@ function handleClientReady(client, message)
"fullWidth": false,
"hideSidebar": false
},
"abiwordAvailable": abiwordAvailable,
"abiwordAvailable": settings.abiwordAvailable(),
"hooks": {}
}
@ -806,24 +800,26 @@ function handleClientReady(client, message)
clientVars.userName = authorName;
}
//This is a reconnect, so we don't have to send the client the ClientVars again
if(message.reconnect == true)
if(sessioninfos[client.id] !== undefined)
{
//Save the revision in sessioninfos, we take the revision from the info the client send to us
sessioninfos[client.id].rev = message.client_rev;
//This is a reconnect, so we don't have to send the client the ClientVars again
if(message.reconnect == true)
{
//Save the revision in sessioninfos, we take the revision from the info the client send to us
sessioninfos[client.id].rev = message.client_rev;
}
//This is a normal first connect
else
{
//Send the clientVars to the Client
client.json.send(clientVars);
//Save the revision in sessioninfos
sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
}
//Save the revision and the author id in sessioninfos
sessioninfos[client.id].author = author;
}
//This is a normal first connect
else
{
//Send the clientVars to the Client
client.json.send(clientVars);
//Save the revision in sessioninfos
sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
}
//Save the revision and the author id in sessioninfos
sessioninfos[client.id].rev = pad.getHeadRevisionNumber();
sessioninfos[client.id].author = author;
//prepare the notification for the other users on the pad, that this user joined
var messageToTheOtherUsers = {
@ -859,16 +855,18 @@ function handleClientReady(client, message)
{
authorManager.getAuthorColorId(sessioninfos[sessionID].author, function(err, value)
{
if(ERR(err, callback)) return;
sessionAuthorColorId = value;
callback(err);
callback();
})
},
function(callback)
{
authorManager.getAuthorName(sessioninfos[sessionID].author, function(err, value)
{
if(ERR(err, callback)) return;
sessionAuthorName = value;
callback(err);
callback();
})
}
],callback);
@ -903,6 +901,6 @@ function handleClientReady(client, message)
}
],function(err)
{
if(err) throw err;
ERR(err);
});
}

View file

@ -19,6 +19,7 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var log4js = require('log4js');
var messageLogger = log4js.getLogger("message");
var securityManager = require("../db/SecurityManager");
@ -109,7 +110,7 @@ exports.setSocketIO = function(_socket)
{
securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, function(err, statusObject)
{
if(err) throw err;
ERR(err);
//access was granted, mark the client as authorized and handle the message
if(statusObject.accessStatus == "grant")

View file

@ -18,10 +18,12 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var async = require("async");
var padManager = require("../db/PadManager");
var Changeset = require("../utils/Changeset");
var AttributePoolFactory = require("../utils/AttributePoolFactory");
var settings = require('../utils/Settings');
var authorManager = require("../db/AuthorManager");
var log4js = require('log4js');
var messageLogger = log4js.getLogger("message");
@ -92,7 +94,7 @@ function handleClientReady(client, message)
//send the timeslider client the clientVars, with this values its able to start
createTimesliderClientVars (message.padId, function(err, clientVars)
{
if(err) throw err;
ERR(err);
client.json.send({type: "CLIENT_VARS", data: clientVars});
})
@ -138,7 +140,7 @@ function handleChangesetRequest(client, message)
//build the requested rough changesets and send them back
getChangesetInfo(padId, start, end, granularity, function(err, changesetInfo)
{
if(err) throw err;
ERR(err);
var data = changesetInfo;
data.requestID = message.data.requestID;
@ -159,6 +161,7 @@ function createTimesliderClientVars (padId, callback)
fullWidth: false,
disableRightBar: false,
initialChangesets: [],
abiwordAvailable: settings.abiwordAvailable(),
hooks: [],
initialStyledContents: {}
};
@ -171,8 +174,9 @@ function createTimesliderClientVars (padId, callback)
{
padManager.getPad(padId, function(err, _pad)
{
if(ERR(err, callback)) return;
pad = _pad;
callback(err);
callback();
});
},
//get all authors and add them to
@ -187,15 +191,17 @@ function createTimesliderClientVars (padId, callback)
{
authorManager.getAuthor(authorId, function(err, author)
{
if(ERR(err, callback)) return;
historicalAuthorData[authorId] = author;
callback(err);
callback();
});
}, function(err)
{
if(ERR(err, callback)) return;
//add historicalAuthorData to the clientVars and continue
clientVars.historicalAuthorData = historicalAuthorData;
clientVars.initialStyledContents.historicalAuthorData = historicalAuthorData;
callback(err);
callback();
});
},
//get the timestamp of the last revision
@ -203,8 +209,9 @@ function createTimesliderClientVars (padId, callback)
{
pad.getRevisionDate(pad.getHeadRevisionNumber(), function(err, date)
{
if(ERR(err, callback)) return;
clientVars.currentTime = date;
callback(err);
callback();
});
},
function(callback)
@ -235,14 +242,16 @@ function createTimesliderClientVars (padId, callback)
Math.floor(lastRev / topGranularity)*topGranularity+topGranularity, granularity,
function(err, changeset)
{
if(ERR(err, callback)) return;
clientVars.initialChangesets.push(changeset);
callback(err);
callback();
});
}, callback);
}
], function(err)
{
callback(err, clientVars);
if(ERR(err, callback)) return;
callback(null, clientVars);
});
}
@ -267,8 +276,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
{
padManager.getPad(padId, function(err, _pad)
{
if(ERR(err, callback)) return;
pad = _pad;
callback(err);
callback();
});
},
function(callback)
@ -309,8 +319,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
{
composePadChangesets(padId, item.start, item.end, function(err, changeset)
{
if(ERR(err, callback)) return;
composedChangesets[item.start + "/" + item.end] = changeset;
callback(err);
callback();
});
}, callback);
},
@ -321,8 +332,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
{
pad.getRevisionDate(revNum, function(err, revDate)
{
if(ERR(err, callback)) return;
revisionDate[revNum] = Math.floor(revDate/1000);
callback(err);
callback();
});
}, callback);
},
@ -331,8 +343,9 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
{
getPadLines(padId, startNum-1, function(err, _lines)
{
if(ERR(err, callback)) return;
lines = _lines;
callback(err);
callback();
});
}
], callback);
@ -383,20 +396,15 @@ function getChangesetInfo(padId, startNum, endNum, granularity, callback)
}
], function(err)
{
if(err)
{
callback(err);
}
else
{
callback(null, {forwardsChangesets: forwardsChangesets,
backwardsChangesets: backwardsChangesets,
apool: apool.toJsonable(),
actualEndNum: endNum,
timeDeltas: timeDeltas,
start: startNum,
granularity: granularity });
}
if(ERR(err, callback)) return;
callback(null, {forwardsChangesets: forwardsChangesets,
backwardsChangesets: backwardsChangesets,
apool: apool.toJsonable(),
actualEndNum: endNum,
timeDeltas: timeDeltas,
start: startNum,
granularity: granularity });
});
}
@ -416,8 +424,9 @@ function getPadLines(padId, revNum, callback)
{
padManager.getPad(padId, function(err, _pad)
{
if(ERR(err, callback)) return;
pad = _pad;
callback(err);
callback();
});
},
//get the atext
@ -427,8 +436,9 @@ function getPadLines(padId, revNum, callback)
{
pad.getInternalRevisionAText(revNum, function(err, _atext)
{
if(ERR(err, callback)) return;
atext = _atext;
callback(err);
callback();
});
}
else
@ -445,7 +455,8 @@ function getPadLines(padId, revNum, callback)
}
], function(err)
{
callback(err, result);
if(ERR(err, callback)) return;
callback(null, result);
});
}
@ -465,8 +476,9 @@ function composePadChangesets(padId, startNum, endNum, callback)
{
padManager.getPad(padId, function(err, _pad)
{
if(ERR(err, callback)) return;
pad = _pad;
callback(err);
callback();
});
},
//fetch all changesets we need
@ -486,8 +498,9 @@ function composePadChangesets(padId, startNum, endNum, callback)
{
pad.getRevisionChangeset(revNum, function(err, value)
{
if(ERR(err, callback)) return;
changesets[revNum] = value;
callback(err);
callback();
});
},callback);
},
@ -509,7 +522,7 @@ function composePadChangesets(padId, startNum, endNum, callback)
//return err and changeset
function(err)
{
if(err) throw err;
callback(err, changeset);
if(ERR(err, callback)) return;
callback(null, changeset);
});
}

View file

@ -20,6 +20,7 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var log4js = require('log4js');
var os = require("os");
var socketio = require('socket.io');
@ -91,6 +92,9 @@ async.waterfall([
var httpLogger = log4js.getLogger("http");
app.configure(function()
{
// Activate http basic auth if it has been defined in settings.json
if(settings.httpAuth != null) app.use(basic_auth);
// 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.
if (!(settings.loglevel === "WARN" || settings.loglevel == "ERROR"))
@ -98,6 +102,12 @@ async.waterfall([
app.use(express.cookieParser());
});
app.error(function(err, req, res, next){
res.send(500);
console.error(err.stack ? err.stack : err.toString());
gracefulShutdown();
});
//serve static files
app.get('/static/*', function(req, res)
{
@ -129,7 +139,7 @@ async.waterfall([
{
securityManager.checkAccess(req.params.pad, req.cookies.sessionid, req.cookies.token, req.cookies.password, function(err, accessObj)
{
if(err) throw err;
if(ERR(err, callback)) return;
//there is access, continue
if(accessObj.accessStatus == "grant")
@ -143,6 +153,26 @@ async.waterfall([
}
});
}
//checks for basic http auth
function basic_auth (req, res, next) {
if (req.headers.authorization && req.headers.authorization.search('Basic ') === 0) {
// fetch login and password
if (new Buffer(req.headers.authorization.split(' ')[1], 'base64').toString() == settings.httpAuth) {
next();
return;
}
}
res.header('WWW-Authenticate', 'Basic realm="Protected Area"');
if (req.headers.authorization) {
setTimeout(function () {
res.send('Authentication required', 401);
}, 1000);
} else {
res.send('Authentication required', 401);
}
}
//serve read only pad
app.get('/ro/:id', function(req, res)
@ -159,12 +189,14 @@ async.waterfall([
{
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(err);
callback();
});
},
//render the html document
@ -182,8 +214,9 @@ async.waterfall([
//render the html document
exporthtml.getPadHTMLDocument(padId, null, false, function(err, _html)
{
if(ERR(err, callback)) return;
html = _html;
callback(err);
callback();
});
});
}
@ -191,7 +224,7 @@ async.waterfall([
{
//throw any unexpected error
if(err && err != "notfound")
throw err;
ERR(err);
if(err == "notfound")
res.send('404 - Not Found', 404);
@ -199,100 +232,108 @@ async.waterfall([
res.send(html);
});
});
//serve pad.html under /p
app.get('/p/:pad', function(req, res, next)
{
//redirects browser to the pad's sanitized url if needed. otherwise, renders the html
function goToPad(req, res, render) {
//ensure the padname is valid and the url doesn't end with a /
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
{
res.send('Such a padname is forbidden', 404);
return;
}
res.header("Server", serverName);
var filePath = path.normalize(__dirname + "/../static/pad.html");
res.sendfile(filePath, { maxAge: exports.maxAge });
else
{
padManager.sanitizePadId(req.params.pad, function(padId) {
//the pad id was sanitized, so we redirect to the sanitized version
if(padId != req.params.pad)
{
var real_path = req.path.replace(/^\/p\/[^\/]+/, '/p/' + padId);
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
{
render();
}
});
}
}
//serve pad.html under /p
app.get('/p/:pad', function(req, res, next)
{
goToPad(req, res, function() {
res.header("Server", serverName);
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)
{
//ensure the padname is valid and the url doesn't end with a /
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
{
res.send('Such a padname is forbidden', 404);
return;
}
res.header("Server", serverName);
var filePath = path.normalize(__dirname + "/../static/timeslider.html");
res.sendfile(filePath, { maxAge: exports.maxAge });
goToPad(req, res, function() {
res.header("Server", serverName);
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/export/:type', function(req, res, next)
app.get('/p/:pad/:rev?/export/:type', function(req, res, next)
{
//ensure the padname is valid and the url doesn't end with a /
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
{
res.send('Such a padname is forbidden', 404);
return;
}
var types = ["pdf", "doc", "txt", "html", "odt"];
//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 && req.params.type != "html" && req.params.type != "txt" )
{
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", "*");
res.header("Server", serverName);
hasPadAccess(req, res, function()
{
exportHandler.doExport(req, res, req.params.pad, req.params.type);
goToPad(req, res, function() {
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", "*");
res.header("Server", serverName);
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)
{
//ensure the padname is valid and the url doesn't end with a /
if(!padManager.isValidPadId(req.params.pad) || /\/$/.test(req.url))
{
res.send('Such a padname is forbidden', 404);
return;
}
//if abiword is disabled, skip handling this request
if(settings.abiword == null)
{
next();
return;
}
goToPad(req, res, function() {
//if abiword is disabled, skip handling this request
if(settings.abiword == null)
{
next();
return;
}
res.header("Server", serverName);
hasPadAccess(req, res, function()
{
importHandler.doImport(req, res, req.params.pad);
res.header("Server", serverName);
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) {
var apiCaller = function(req, res, fields)
{
res.header("Server", serverName);
res.header("Content-Type", "application/json; charset=utf-8");

View file

@ -3,6 +3,12 @@
* Can be found in https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js
*/
/**
* This code is mostly from the old Etherpad. Please help us to comment this code.
* This helps other people to understand this code better and helps them to improve it.
* TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED
*/
/*
* Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
*

View file

@ -0,0 +1,344 @@
/**
* Copyright 2011 Adrian Lang
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var async = require("async");
var Changeset = require("./Changeset");
var padManager = require("../db/PadManager");
function getPadDokuWiki(pad, revNum, callback)
{
var atext = pad.atext;
var dokuwiki;
async.waterfall([
// fetch revision atext
function (callback)
{
if (revNum != undefined)
{
pad.getInternalRevisionAText(revNum, function (err, revisionAtext)
{
atext = revisionAtext;
callback(err);
});
}
else
{
callback(null);
}
},
// convert atext to dokuwiki text
function (callback)
{
dokuwiki = getDokuWikiFromAtext(pad, atext);
callback(null);
}],
// run final callback
function (err)
{
callback(err, dokuwiki);
});
}
function getDokuWikiFromAtext(pad, atext)
{
var apool = pad.apool();
var textLines = atext.text.slice(0, -1).split('\n');
var attribLines = Changeset.splitAttributionLines(atext.attribs, atext.text);
var tags = ['======', '=====', '**', '//', '__', 'del>'];
var props = ['heading1', 'heading2', 'bold', 'italic', 'underline', 'strikethrough'];
var anumMap = {};
props.forEach(function (propName, i)
{
var propTrueNum = apool.putAttrib([propName, true], true);
if (propTrueNum >= 0)
{
anumMap[propTrueNum] = i;
}
});
function getLineDokuWiki(text, attribs)
{
var propVals = [false, false, false];
var ENTER = 1;
var STAY = 2;
var LEAVE = 0;
// Use order of tags (b/i/u) as order of nesting, for simplicity
// and decent nesting. For example,
// <b>Just bold<b> <b><i>Bold and italics</i></b> <i>Just italics</i>
// becomes
// <b>Just bold <i>Bold and italics</i></b> <i>Just italics</i>
var taker = Changeset.stringIterator(text);
var assem = Changeset.stringAssembler();
function emitOpenTag(i)
{
if (tags[i].indexOf('>') !== -1) {
assem.append('<');
}
assem.append(tags[i]);
}
function emitCloseTag(i)
{
if (tags[i].indexOf('>') !== -1) {
assem.append('</');
}
assem.append(tags[i]);
}
var urls = _findURLs(text);
var idx = 0;
function processNextChars(numChars)
{
if (numChars <= 0)
{
return;
}
var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars));
idx += numChars;
while (iter.hasNext())
{
var o = iter.next();
var propChanged = false;
Changeset.eachAttribNumber(o.attribs, function (a)
{
if (a in anumMap)
{
var i = anumMap[a]; // i = 0 => bold, etc.
if (!propVals[i])
{
propVals[i] = ENTER;
propChanged = true;
}
else
{
propVals[i] = STAY;
}
}
});
for (var i = 0; i < propVals.length; i++)
{
if (propVals[i] === true)
{
propVals[i] = LEAVE;
propChanged = true;
}
else if (propVals[i] === STAY)
{
propVals[i] = true; // set it back
}
}
// now each member of propVal is in {false,LEAVE,ENTER,true}
// according to what happens at start of span
if (propChanged)
{
// leaving bold (e.g.) also leaves italics, etc.
var left = false;
for (var i = 0; i < propVals.length; i++)
{
var v = propVals[i];
if (!left)
{
if (v === LEAVE)
{
left = true;
}
}
else
{
if (v === true)
{
propVals[i] = STAY; // tag will be closed and re-opened
}
}
}
for (var i = propVals.length - 1; i >= 0; i--)
{
if (propVals[i] === LEAVE)
{
emitCloseTag(i);
propVals[i] = false;
}
else if (propVals[i] === STAY)
{
emitCloseTag(i);
}
}
for (var i = 0; i < propVals.length; i++)
{
if (propVals[i] === ENTER || propVals[i] === STAY)
{
emitOpenTag(i);
propVals[i] = true;
}
}
// propVals is now all {true,false} again
} // end if (propChanged)
var chars = o.chars;
if (o.lines)
{
chars--; // exclude newline at end of line, if present
}
var s = taker.take(chars);
assem.append(_escapeDokuWiki(s));
} // end iteration over spans in line
for (var i = propVals.length - 1; i >= 0; i--)
{
if (propVals[i])
{
emitCloseTag(i);
propVals[i] = false;
}
}
} // end processNextChars
if (urls)
{
urls.forEach(function (urlData)
{
var startIndex = urlData[0];
var url = urlData[1];
var urlLength = url.length;
processNextChars(startIndex - idx);
assem.append('[[');
// Do not use processNextChars since a link does not contain syntax and
// needs no escaping
var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + urlLength));
idx += urlLength;
assem.append(taker.take(iter.next().chars));
assem.append(']]');
});
}
processNextChars(text.length - idx);
return assem.toString() + "\n";
} // end getLineDokuWiki
var pieces = [];
for (var i = 0; i < textLines.length; i++)
{
var line = _analyzeLine(textLines[i], attribLines[i], apool);
var lineContent = getLineDokuWiki(line.text, line.aline);
if (line.listLevel && lineContent)
{
pieces.push(new Array(line.listLevel + 1).join(' ') + '* ');
}
pieces.push(lineContent);
}
return pieces.join('');
}
function _analyzeLine(text, aline, apool)
{
var line = {};
// identify list
var lineMarker = 0;
line.listLevel = 0;
if (aline)
{
var opIter = Changeset.opIterator(aline);
if (opIter.hasNext())
{
var listType = Changeset.opAttributeValue(opIter.next(), 'list', apool);
if (listType)
{
lineMarker = 1;
listType = /([a-z]+)([12345678])/.exec(listType);
if (listType)
{
line.listTypeName = listType[1];
line.listLevel = Number(listType[2]);
}
}
}
}
if (lineMarker)
{
line.text = text.substring(1);
line.aline = Changeset.subattribution(aline, 1);
}
else
{
line.text = text;
line.aline = aline;
}
return line;
}
exports.getPadDokuWikiDocument = function (padId, revNum, callback)
{
padManager.getPad(padId, function (err, pad)
{
if (err)
{
callback(err);
return;
}
getPadDokuWiki(pad, revNum, callback);
});
}
function _escapeDokuWiki(s)
{
s = s.replace(/(\/\/|\*\*|__)/g, '%%$1%%');
return s;
}
// copied from ACE
var _REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/;
var _REGEX_SPACE = /\s/;
var _REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + _REGEX_WORDCHAR.source + ')');
var _REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:)/.source + _REGEX_URLCHAR.source + '*(?![:.,;])' + _REGEX_URLCHAR.source, 'g');
// returns null if no URLs, or [[startIndex1, url1], [startIndex2, url2], ...]
function _findURLs(text)
{
_REGEX_URL.lastIndex = 0;
var urls = null;
var execResult;
while ((execResult = _REGEX_URL.exec(text)))
{
urls = (urls || []);
var startIndex = execResult.index;
var url = execResult[0];
urls.push([startIndex, url]);
}
return urls;
}

View file

@ -17,6 +17,7 @@
var async = require("async");
var Changeset = require("./Changeset");
var padManager = require("../db/PadManager");
var ERR = require("async-stacktrace");
function getPadPlainText(pad, revNum)
{
@ -58,8 +59,9 @@ function getPadHTML(pad, revNum, callback)
{
pad.getInternalRevisionAText(revNum, function (err, revisionAtext)
{
if(ERR(err, callback)) return;
atext = revisionAtext;
callback(err);
callback();
});
}
else
@ -81,7 +83,8 @@ function getPadHTML(pad, revNum, callback)
function (err)
{
callback(err, html);
if(ERR(err, callback)) return;
callback(null, html);
});
}
@ -459,11 +462,7 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
{
padManager.getPad(padId, function (err, pad)
{
if (err)
{
callback(err);
return;
}
if(ERR(err, callback)) return;
var head =
(noDocType ? '' : '<!doctype html>\n') +
@ -488,7 +487,8 @@ exports.getPadHTMLDocument = function (padId, revNum, noDocType, callback)
getPadHTML(pad, revNum, function (err, html)
{
callback(err, head + html + foot);
if(ERR(err, callback)) return;
callback(null, head + html + foot);
});
});
}

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
var jsdom = require('jsdom').jsdom;
var jsdom = require('jsdom-nocontextifiy').jsdom;
var log4js = require('log4js');
var Changeset = require("./Changeset");

View file

@ -19,6 +19,7 @@
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var settings = require('./Settings');
var async = require('async');
var fs = require('fs');
@ -33,7 +34,7 @@ var os = require('os');
var padJS = ["jquery.min.js", "pad_utils.js", "plugins.js", "undo-xpopup.js", "json2.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "ace.js", "collab_client.js", "pad_userlist.js", "pad_impexp.js", "pad_savedrevs.js", "pad_connectionstatus.js", "pad2.js", "jquery-ui.js", "chat.js", "excanvas.js", "farbtastic.js"];
var timesliderJS = ["jquery.min.js", "plugins.js", "undo-xpopup.js", "json2.js", "colorutils.js", "draggable.js", "pad_utils.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "easysync2_client.js", "domline_client.js", "linestylefilter_client.js", "cssmanager_client.js", "broadcast.js", "broadcast_slider.js", "broadcast_revisions.js"];
var timesliderJS = ["jquery.min.js", "plugins.js", "undo-xpopup.js", "json2.js", "colorutils.js", "draggable.js", "pad_utils.js", "pad_cookie.js", "pad_editor.js", "pad_editbar.js", "pad_docbar.js", "pad_modals.js", "pad_impexp.js", "easysync2_client.js", "domline_client.js", "linestylefilter_client.js", "cssmanager_client.js", "broadcast.js", "broadcast_slider.js", "broadcast_revisions.js"];
/**
* creates the minifed javascript for the given minified name
@ -77,7 +78,7 @@ exports.minifyJS = function(req, res, jsFilename)
//read the files in the folder
fs.readdir(path, function(err, files)
{
if(err) { callback(err); return; }
if(ERR(err, callback)) return;
//we wanna check the directory itself for changes too
files.push(".");
@ -88,7 +89,7 @@ exports.minifyJS = function(req, res, jsFilename)
//get the stat data of this file
fs.stat(path + "/" + filename, function(err, stats)
{
if(err) { callback(err); return; }
if(ERR(err, callback)) return;
//get the modification time
var modificationTime = stats.mtime.getTime();
@ -110,7 +111,11 @@ exports.minifyJS = function(req, res, jsFilename)
//check the modification time of the minified js
fs.stat("../var/minified_" + jsFilename, function(err, stats)
{
if(err && err.code != "ENOENT") callback(err);
if(err && err.code != "ENOENT")
{
ERR(err, callback);
return;
}
//there is no minfied file or there new changes since this file was generated, so continue generating this file
if((err && err.code == "ENOENT") || stats.mtime.getTime() < latestModification)
@ -131,8 +136,9 @@ exports.minifyJS = function(req, res, jsFilename)
{
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
{
if(ERR(err, callback)) return;
fileValues[item] = data;
callback(err);
callback();
});
}, callback);
},
@ -161,6 +167,8 @@ exports.minifyJS = function(req, res, jsFilename)
//read the included file
fs.readFile(filename, "utf-8", function(err, data)
{
if(ERR(err, callback)) return;
//compress the file
if(type == "JS")
{
@ -188,17 +196,19 @@ exports.minifyJS = function(req, res, jsFilename)
embeds[item] = embeds[item].substr(0, embeds[item].length-1);
embeds[item] = "'" + embeds[item] + "'";
callback(err);
callback();
});
}, function(err)
{
if(ERR(err, callback)) return;
//replace the include command with the include
for(var i in embeds)
{
fileValues["ace.js"]=fileValues["ace.js"].replace(i, embeds[i]);
}
callback(err);
callback();
});
},
//put all together and write it into a file
@ -227,7 +237,10 @@ exports.minifyJS = function(req, res, jsFilename)
if(os.type().indexOf("Windows") == -1)
{
gzip(result, 9, function(err, compressedResult){
if(err) {callback(err); return}
//weird gzip bug that returns 0 instead of null if everything is ok
err = err === 0 ? null : err;
if(ERR(err, callback)) return;
fs.writeFile("../var/minified_" + jsFilename + ".gz", compressedResult, callback);
});
@ -242,7 +255,10 @@ exports.minifyJS = function(req, res, jsFilename)
}
], function(err)
{
if(err && err != "stop") throw err;
if(err && err != "stop")
{
if(ERR(err)) return;
}
//check if gzip is supported by this browser
var gzipSupport = req.header('Accept-Encoding', '').indexOf('gzip') != -1;
@ -270,15 +286,16 @@ exports.minifyJS = function(req, res, jsFilename)
async.forEach(jsFiles, function (item, callback)
{
fs.readFile("../static/js/" + item, "utf-8", function(err, data)
{
{
if(ERR(err, callback)) return;
fileValues[item] = data;
callback(err);
callback();
});
},
//send all files together
function(err)
{
if(err) throw err;
if(ERR(err)) return;
for(var i=0;i<jsFiles.length;i++)
{

View file

@ -20,6 +20,7 @@
*/
var fs = require("fs");
var os = require("os");
/**
* The IP ep-lite should listen to
@ -68,6 +69,24 @@ exports.abiword = null;
*/
exports.loglevel = "INFO";
/**
* Http basic auth, with "user:password" format
*/
exports.httpAuth = null;
//checks if abiword is avaiable
exports.abiwordAvailable = function()
{
if(exports.abiword != null)
{
return os.type().indexOf("Windows") != -1 ? "withoutPDF" : "yes";
}
else
{
return "no";
}
}
//read the settings sync
var settingsStr = fs.readFileSync("../settings.json").toString();

17
node/utils/customError.js Normal file
View file

@ -0,0 +1,17 @@
/*
This helper modules allows us to create different type of errors we can throw
*/
function customError(message, errorName)
{
this.name = errorName || "Error";
this.message = message;
var stackParts = new Error().stack.split("\n");
stackParts.splice(0,2);
stackParts.unshift(this.name + ": " + message);
this.stack = stackParts.join("\n");
}
customError.prototype = Error.prototype;
module.exports = customError;