prepare to async: trivial reformatting

This change is only cosmetic. Its aim is do make it easier to understand the
async changes that are going to be merged later on. It was extracted from the
original work from Ray Bellis.

To verify that nothing has changed, you can run the following command on each
file touched by this commit:
  npm install uglify-es
  diff --unified <(uglify-js --beautify bracketize <BEFORE.js>) <(uglify-js --beautify bracketize <AFTER.js>)



This is a complete script that does the same automatically (works from a
mercurial clone):

```bash
#!/usr/bin/env bash

set -eu

REVISION=<THIS_REVISION>

PARENT_REV=$(hg identify --rev "${REVISION}" --template '{p1rev}')
FILE_LIST=$(hg status --no-status --change ${REVISION})
UGLIFYJS="node_modules/uglify-es/bin/uglifyjs"

for FILE_NAME in ${FILE_LIST[@]}; do
  echo "Checking ${FILE_NAME}"
  diff --unified \
    <("${UGLIFYJS}" --beautify bracketize <(hg cat --rev "${PARENT_REV}" "${FILE_NAME}")) \
    <("${UGLIFYJS}" --beautify bracketize <(hg cat --rev "${REVISION}"   "${FILE_NAME}"))
done
```
This commit is contained in:
muxator 2019-02-08 23:20:57 +01:00
parent cc23bd18a4
commit 9497ee734f
33 changed files with 2706 additions and 2943 deletions

View file

@ -1,25 +1,25 @@
/* /*
This is a debug tool. It checks all revisions for data corruption * This is a debug tool. It checks all revisions for data corruption
*/ */
if(process.argv.length != 2) if (process.argv.length != 2) {
{
console.error("Use: node bin/checkAllPads.js"); console.error("Use: node bin/checkAllPads.js");
process.exit(1); process.exit(1);
} }
// initialize the variables // initialize the variables
var db, settings, padManager; var db, settings, padManager;
var npm = require("../src/node_modules/npm"); var npm = require('../src/node_modules/npm');
var async = require("../src/node_modules/async"); var async = require('../src/node_modules/async');
var Changeset = require("../src/static/js/Changeset"); var Changeset = require('../src/static/js/Changeset');
async.series([ async.series([
// load npm // load npm
function(callback) { function(callback) {
npm.load({}, callback); npm.load({}, callback);
}, },
// load modules // load modules
function(callback) { function(callback) {
settings = require('../src/node/utils/Settings'); settings = require('../src/node/utils/Settings');
@ -28,31 +28,29 @@ async.series([
// initialize the database // initialize the database
db.init(callback); db.init(callback);
}, },
// load pads // load pads
function (callback) function (callback) {
{
padManager = require('../src/node/db/PadManager'); padManager = require('../src/node/db/PadManager');
padManager.listAllPads(function(err, res) padManager.listAllPads(function(err, res) {
{
padIds = res.padIDs; padIds = res.padIDs;
callback(err); callback(err);
}); });
}, },
function (callback)
{ function (callback) {
async.forEach(padIds, function(padId, callback) async.forEach(padIds, function(padId, callback) {
{
padManager.getPad(padId, function(err, pad) { padManager.getPad(padId, function(err, pad) {
if (err) { if (err) {
callback(err); callback(err);
} }
// check if the pad has a pool // check if the pad has a pool
if(pad.pool === undefined ) if (pad.pool === undefined ) {
{
console.error("[" + pad.id + "] Missing attribute pool"); console.error("[" + pad.id + "] Missing attribute pool");
callback(); callback();
return; return;
} }
@ -60,36 +58,32 @@ async.series([
// key revisions always save the full pad atext // key revisions always save the full pad atext
var head = pad.getHeadRevisionNumber(); var head = pad.getHeadRevisionNumber();
var keyRevisions = []; var keyRevisions = [];
for(var i=0;i<head;i+=100) for (var i = 0; i < head; i += 100) {
{
keyRevisions.push(i); keyRevisions.push(i);
} }
//run trough all key revisions // run through all key revisions
async.forEachSeries(keyRevisions, function(keyRev, callback) async.forEachSeries(keyRevisions, function(keyRev, callback) {
{
// create an array of revisions we need till the next keyRevision or the End // create an array of revisions we need till the next keyRevision or the End
var revisionsNeeded = []; var revisionsNeeded = [];
for(var i=keyRev;i<=keyRev+100 && i<=head; i++)
{ for(var i = keyRev; i <= keyRev + 100 && i <= head; i++) {
revisionsNeeded.push(i); revisionsNeeded.push(i);
} }
// this array will hold all revision changesets // this array will hold all revision changesets
var revisions = []; var revisions = [];
//run trough all needed revisions and get them from the database // run through all needed revisions and get them from the database
async.forEach(revisionsNeeded, function(revNum, callback) async.forEach(revisionsNeeded, function(revNum, callback) {
{ db.db.get("pad:" + pad.id + ":revs:" + revNum, function(err, revision) {
db.db.get("pad:"+pad.id+":revs:" + revNum, function(err, revision)
{
revisions[revNum] = revision; revisions[revNum] = revision;
callback(err); callback(err);
}); });
}, function(err) },
{
if(err) function(err) {
{ if (err) {
callback(err); callback(err);
return; return;
} }
@ -102,8 +96,7 @@ async.series([
} }
// check if there is a atext in the keyRevisions // check if there is a atext in the keyRevisions
if(revisions[keyRev].meta === undefined || revisions[keyRev].meta.atext === undefined) if (revisions[keyRev].meta === undefined || revisions[keyRev].meta.atext === undefined) {
{
console.error("[" + pad.id + "] Missing atext in revision " + keyRev); console.error("[" + pad.id + "] Missing atext in revision " + keyRev);
callback(); callback();
return; return;
@ -112,16 +105,12 @@ async.series([
var apool = pad.pool; var apool = pad.pool;
var atext = revisions[keyRev].meta.atext; var atext = revisions[keyRev].meta.atext;
for(var i=keyRev+1;i<=keyRev+100 && i<=head; i++) for(var i = keyRev + 1; i <= keyRev + 100 && i <= head; i++) {
{ try {
try
{
// console.log("[" + pad.id + "] check revision " + i); // console.log("[" + pad.id + "] check revision " + i);
var cs = revisions[i].changeset; var cs = revisions[i].changeset;
atext = Changeset.applyToAText(cs, atext, apool); atext = Changeset.applyToAText(cs, atext, apool);
} } catch(e) {
catch(e)
{
console.error("[" + pad.id + "] Bad changeset at revision " + i + " - " + e.message); console.error("[" + pad.id + "] Bad changeset at revision " + i + " - " + e.message);
callback(); callback();
return; return;
@ -134,11 +123,11 @@ async.series([
}); });
}, callback); }, callback);
} }
], function (err) ],
{ function (err) {
if(err) throw err; if (err) {
else throw err;
{ } else {
console.log("finished"); console.log("finished");
process.exit(0); process.exit(0);
} }

View file

@ -1,29 +1,30 @@
/* /*
This is a debug tool. It checks all revisions for data corruption * This is a debug tool. It checks all revisions for data corruption
*/ */
if(process.argv.length != 3) if (process.argv.length != 3) {
{
console.error("Use: node bin/checkPad.js $PADID"); console.error("Use: node bin/checkPad.js $PADID");
process.exit(1); process.exit(1);
} }
//get the padID //get the padID
var padId = process.argv[2]; var padId = process.argv[2];
// initialize the variables // initialize the variables
var db, settings, padManager; var db, settings, padManager;
var npm = require("../src/node_modules/npm"); var npm = require('../src/node_modules/npm');
var async = require("../src/node_modules/async"); var async = require('../src/node_modules/async');
var Changeset = require("ep_etherpad-lite/static/js/Changeset"); var Changeset = require('ep_etherpad-lite/static/js/Changeset');
async.series([ async.series([
// load npm // load npm
function(callback) { function(callback) {
npm.load({}, function(er) { npm.load({}, function(er) {
callback(er); callback(er);
}) });
}, },
// load modules // load modules
function(callback) { function(callback) {
settings = require('../src/node/utils/Settings'); settings = require('../src/node/utils/Settings');
@ -32,76 +33,65 @@ async.series([
// initialize the database // initialize the database
db.init(callback); db.init(callback);
}, },
// get the pad // get the pad
function (callback) function (callback) {
{
padManager = require('../src/node/db/PadManager'); padManager = require('../src/node/db/PadManager');
padManager.doesPadExists(padId, function(err, exists) padManager.doesPadExists(padId, function(err, exists) {
{ if (!exists) {
if(!exists)
{
console.error("Pad does not exist"); console.error("Pad does not exist");
process.exit(1); process.exit(1);
} }
padManager.getPad(padId, function(err, _pad) padManager.getPad(padId, function(err, _pad) {
{
pad = _pad; pad = _pad;
callback(err); callback(err);
}); });
}); });
}, },
function (callback)
{ function (callback) {
// create an array with key revisions // create an array with key revisions
// key revisions always save the full pad atext // key revisions always save the full pad atext
var head = pad.getHeadRevisionNumber(); var head = pad.getHeadRevisionNumber();
var keyRevisions = []; var keyRevisions = [];
for(var i=0;i<head;i+=100) for (var i = 0; i < head; i += 100) {
{
keyRevisions.push(i); keyRevisions.push(i);
} }
//run trough all key revisions // run through all key revisions
async.forEachSeries(keyRevisions, function(keyRev, callback) async.forEachSeries(keyRevisions, function(keyRev, callback) {
{
// create an array of revisions we need till the next keyRevision or the End // create an array of revisions we need till the next keyRevision or the End
var revisionsNeeded = []; var revisionsNeeded = [];
for(var i=keyRev;i<=keyRev+100 && i<=head; i++) for(var i = keyRev; i <= keyRev + 100 && i <= head; i++) {
{
revisionsNeeded.push(i); revisionsNeeded.push(i);
} }
// this array will hold all revision changesets // this array will hold all revision changesets
var revisions = []; var revisions = [];
//run trough all needed revisions and get them from the database // run through all needed revisions and get them from the database
async.forEach(revisionsNeeded, function(revNum, callback) async.forEach(revisionsNeeded, function(revNum, callback) {
{ db.db.get("pad:" + padId + ":revs:" + revNum, function(err, revision) {
db.db.get("pad:"+padId+":revs:" + revNum, function(err, revision)
{
revisions[revNum] = revision; revisions[revNum] = revision;
callback(err); callback(err);
}); });
}, function(err) },
{ function(err) {
if(err) if (err) {
{
callback(err); callback(err);
return; return;
} }
// check if the pad has a pool // check if the pad has a pool
if(pad.pool === undefined ) if (pad.pool === undefined) {
{
console.error("Attribute pool is missing"); console.error("Attribute pool is missing");
process.exit(1); process.exit(1);
} }
// check if there is an atext in the keyRevisions // check if there is an atext in the keyRevisions
if(revisions[keyRev] === undefined || revisions[keyRev].meta === undefined || revisions[keyRev].meta.atext === undefined) if (revisions[keyRev] === undefined || revisions[keyRev].meta === undefined || revisions[keyRev].meta.atext === undefined) {
{
console.error("No atext in key revision " + keyRev); console.error("No atext in key revision " + keyRev);
callback(); callback();
return; return;
@ -110,16 +100,12 @@ async.series([
var apool = pad.pool; var apool = pad.pool;
var atext = revisions[keyRev].meta.atext; var atext = revisions[keyRev].meta.atext;
for(var i=keyRev+1;i<=keyRev+100 && i<=head; i++) for (var i = keyRev + 1; i <= keyRev + 100 && i <= head; i++) {
{ try {
try
{
// console.log("check revision " + i); // console.log("check revision " + i);
var cs = revisions[i].changeset; var cs = revisions[i].changeset;
atext = Changeset.applyToAText(cs, atext, apool); atext = Changeset.applyToAText(cs, atext, apool);
} } catch(e) {
catch(e)
{
console.error("Bad changeset at revision " + i + " - " + e.message); console.error("Bad changeset at revision " + i + " - " + e.message);
callback(); callback();
return; return;
@ -130,11 +116,11 @@ async.series([
}); });
}, callback); }, callback);
} }
], function (err) ],
{ function (err) {
if(err) throw err; if(err) {
else throw err;
{ } else {
console.log("finished"); console.log("finished");
process.exit(0); process.exit(0);
} }

View file

@ -1,62 +1,62 @@
/* /*
A tool for deleting pads from the CLI, because sometimes a brick is required to fix a window. * A tool for deleting pads from the CLI, because sometimes a brick is required
* to fix a window.
*/ */
if(process.argv.length != 3) if (process.argv.length != 3) {
{
console.error("Use: node deletePad.js $PADID"); console.error("Use: node deletePad.js $PADID");
process.exit(1); process.exit(1);
} }
// get the padID // get the padID
var padId = process.argv[2]; var padId = process.argv[2];
var db, padManager, pad, settings; var db, padManager, pad, settings;
var neededDBValues = ["pad:"+padId]; var neededDBValues = ["pad:"+padId];
var npm = require("../src/node_modules/npm"); var npm = require('../src/node_modules/npm');
var async = require("../src/node_modules/async"); var async = require('../src/node_modules/async');
async.series([ async.series([
// load npm // load npm
function(callback) { function(callback) {
npm.load({}, function(er) { npm.load({}, function(er) {
if(er) if (er) {
{
console.error("Could not load NPM: " + er) console.error("Could not load NPM: " + er)
process.exit(1); process.exit(1);
} } else {
else
{
callback(); callback();
} }
}) });
}, },
// load modules // load modules
function(callback) { function(callback) {
settings = require('../src/node/utils/Settings'); settings = require('../src/node/utils/Settings');
db = require('../src/node/db/DB'); db = require('../src/node/db/DB');
callback(); callback();
}, },
// initialize the database // initialize the database
function (callback) function (callback) {
{
db.init(callback); db.init(callback);
}, },
// delete the pad and its links // delete the pad and its links
function (callback) function (callback) {
{
padManager = require('../src/node/db/PadManager'); padManager = require('../src/node/db/PadManager');
padManager.removePad(padId, function(err){ padManager.removePad(padId, function(err){
callback(err); callback(err);
}); });
callback(); callback();
} }
], function (err) ],
{ function (err) {
if(err) throw err; if(err) {
else throw err;
{ } else {
console.log("Finished deleting padId: " + padId); console.log("Finished deleting padId: " + padId);
process.exit(); process.exit();
} }

View file

@ -1,87 +1,81 @@
/* /*
This is a debug tool. It helps to extract all datas of a pad and move it from an productive environment and to a develop environment to reproduce bugs there. It outputs a dirtydb file * This is a debug tool. It helps to extract all datas of a pad and move it from
* a productive environment and to a develop environment to reproduce bugs
* there. It outputs a dirtydb file
*/ */
if(process.argv.length != 3) if (process.argv.length != 3) {
{
console.error("Use: node extractPadData.js $PADID"); console.error("Use: node extractPadData.js $PADID");
process.exit(1); process.exit(1);
} }
// get the padID // get the padID
var padId = process.argv[2]; var padId = process.argv[2];
var db, dirty, padManager, pad, settings; var db, dirty, padManager, pad, settings;
var neededDBValues = ["pad:"+padId]; var neededDBValues = ["pad:"+padId];
var npm = require("../node_modules/ep_etherpad-lite/node_modules/npm"); var npm = require('../node_modules/ep_etherpad-lite/node_modules/npm');
var async = require("../node_modules/ep_etherpad-lite/node_modules/async"); var async = require('../node_modules/ep_etherpad-lite/node_modules/async');
async.series([ async.series([
// load npm // load npm
function(callback) { function(callback) {
npm.load({}, function(er) { npm.load({}, function(er) {
if(er) if (er) {
{
console.error("Could not load NPM: " + er) console.error("Could not load NPM: " + er)
process.exit(1); process.exit(1);
} } else {
else
{
callback(); callback();
} }
}) })
}, },
// load modules // load modules
function(callback) { function(callback) {
settings = require('../node_modules/ep_etherpad-lite/node/utils/Settings'); settings = require('../node_modules/ep_etherpad-lite/node/utils/Settings');
db = require('../node_modules/ep_etherpad-lite/node/db/DB'); db = require('../node_modules/ep_etherpad-lite/node/db/DB');
dirty = require("../node_modules/ep_etherpad-lite/node_modules/ueberDB/node_modules/dirty")(padId + ".db"); dirty = require('../node_modules/ep_etherpad-lite/node_modules/ueberDB/node_modules/dirty')(padId + ".db");
callback(); callback();
}, },
// initialize the database // initialize the database
function (callback) function (callback) {
{
db.init(callback); db.init(callback);
}, },
// get the pad // get the pad
function (callback) function (callback) {
{
padManager = require('../node_modules/ep_etherpad-lite/node/db/PadManager'); padManager = require('../node_modules/ep_etherpad-lite/node/db/PadManager');
padManager.getPad(padId, function(err, _pad) padManager.getPad(padId, function(err, _pad) {
{
pad = _pad; pad = _pad;
callback(err); callback(err);
}); });
}, },
function (callback)
{ function (callback) {
// add all authors // add all authors
var authors = pad.getAllAuthors(); var authors = pad.getAllAuthors();
for(var i=0;i<authors.length;i++) for (var i = 0; i < authors.length; i++) {
{ neededDBValues.push('globalAuthor:' + authors[i]);
neededDBValues.push("globalAuthor:" + authors[i]);
} }
// add all revisions // add all revisions
var revHead = pad.head; var revHead = pad.head;
for(var i=0;i<=revHead;i++) for (var i = 0; i <= revHead; i++) {
{ neededDBValues.push('pad:' + padId + ':revs:' + i);
neededDBValues.push("pad:"+padId+":revs:" + i);
} }
// get all chat values // get all chat values
var chatHead = pad.chatHead; var chatHead = pad.chatHead;
for(var i=0;i<=chatHead;i++) for (var i = 0; i <= chatHead; i++) {
{ neededDBValues.push('pad:' + padId + ':chat:' + i);
neededDBValues.push("pad:"+padId+":chat:" + i);
} }
// get and set all values // get and set all values
async.forEach(neededDBValues, function(dbkey, callback) async.forEach(neededDBValues, function(dbkey, callback) {
{ db.db.db.wrappedDB.get(dbkey, function(err, dbvalue) {
db.db.db.wrappedDB.get(dbkey, function(err, dbvalue)
{
if (err) { callback(err); return} if (err) { callback(err); return}
if (dbvalue && typeof dbvalue != 'object') { if (dbvalue && typeof dbvalue != 'object') {
@ -92,18 +86,12 @@ async.series([
}); });
}, callback); }, callback);
} }
], function (err) ],
{ function (err) {
if(err) throw err; if (err) {
else throw err;
{ } else {
console.log("finished"); console.log("finished");
process.exit(); process.exit();
} }
}); });
//get the pad object
//get all revisions of this pad
//get all authors related to this pad
//get the readonly link related to this pad
//get the chat entries related to this pad

View file

@ -105,9 +105,9 @@ Example returns:
*/ */
exports.getAttributePool = function(padID, callback) exports.getAttributePool = function(padID, callback)
{ {
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {pool: pad.pool}); callback(null, {pool: pad.pool});
}); });
} }
@ -128,11 +128,9 @@ Example returns:
exports.getRevisionChangeset = function(padID, rev, callback) exports.getRevisionChangeset = function(padID, rev, callback)
{ {
// check if rev is a number // check if rev is a number
if (rev !== undefined && typeof rev !== "number") if (rev !== undefined && typeof rev !== "number") {
{
// try to parse the number // try to parse the number
if (isNaN(parseInt(rev))) if (isNaN(parseInt(rev))) {
{
callback(new customError("rev is not a number", "apierror")); callback(new customError("rev is not a number", "apierror"));
return; return;
} }
@ -141,37 +139,31 @@ exports.getRevisionChangeset = function(padID, rev, callback)
} }
// ensure this is not a negative number // ensure this is not a negative number
if (rev !== undefined && rev < 0) if (rev !== undefined && rev < 0) {
{
callback(new customError("rev is not a negative number", "apierror")); callback(new customError("rev is not a negative number", "apierror"));
return; return;
} }
// ensure this is not a float value // ensure this is not a float value
if (rev !== undefined && !is_int(rev)) if (rev !== undefined && !is_int(rev)) {
{
callback(new customError("rev is a float value", "apierror")); callback(new customError("rev is a float value", "apierror"));
return; return;
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// the client asked for a special revision // the client asked for a special revision
if(rev !== undefined) if (rev !== undefined) {
{
// check if this is a valid revision // check if this is a valid revision
if(rev > pad.getHeadRevisionNumber()) if (rev > pad.getHeadRevisionNumber()) {
{
callback(new customError("rev is higher than the head revision of the pad", "apierror")); callback(new customError("rev is higher than the head revision of the pad", "apierror"));
return; return;
} }
// get the changeset for this revision // get the changeset for this revision
pad.getRevisionChangeset(rev, function(err, changeset) pad.getRevisionChangeset(rev, function(err, changeset) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, changeset); callback(null, changeset);
@ -181,8 +173,7 @@ exports.getRevisionChangeset = function(padID, rev, callback)
} }
// the client wants the latest changeset, lets return it to him // the client wants the latest changeset, lets return it to him
pad.getRevisionChangeset(pad.getHeadRevisionNumber(), function(err, changeset) pad.getRevisionChangeset(pad.getHeadRevisionNumber(), function(err, changeset) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, changeset); callback(null, changeset);
@ -201,11 +192,9 @@ Example returns:
exports.getText = function(padID, rev, callback) exports.getText = function(padID, rev, callback)
{ {
// check if rev is a number // check if rev is a number
if(rev !== undefined && typeof rev != "number") if (rev !== undefined && typeof rev != "number") {
{
// try to parse the number // try to parse the number
if(isNaN(parseInt(rev))) if (isNaN(parseInt(rev))) {
{
callback(new customError("rev is not a number", "apierror")); callback(new customError("rev is not a number", "apierror"));
return; return;
} }
@ -213,38 +202,32 @@ exports.getText = function(padID, rev, callback)
rev = parseInt(rev); rev = parseInt(rev);
} }
//ensure this is not a negativ number // ensure this is not a negative number
if(rev !== undefined && rev < 0) if (rev !== undefined && rev < 0) {
{
callback(new customError("rev is a negativ number", "apierror")); callback(new customError("rev is a negativ number", "apierror"));
return; return;
} }
// ensure this is not a float value // ensure this is not a float value
if(rev !== undefined && !is_int(rev)) if (rev !== undefined && !is_int(rev)) {
{
callback(new customError("rev is a float value", "apierror")); callback(new customError("rev is a float value", "apierror"));
return; return;
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// the client asked for a special revision // the client asked for a special revision
if(rev !== undefined) if (rev !== undefined) {
{
// check if this is a valid revision // check if this is a valid revision
if(rev > pad.getHeadRevisionNumber()) if (rev > pad.getHeadRevisionNumber()) {
{
callback(new customError("rev is higher than the head revision of the pad", "apierror")); callback(new customError("rev is higher than the head revision of the pad", "apierror"));
return; return;
} }
// get the text of this revision // get the text of this revision
pad.getInternalRevisionAText(rev, function(err, atext) pad.getInternalRevisionAText(rev, function(err, atext) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
var data = {text: atext.text}; var data = {text: atext.text};
@ -273,15 +256,13 @@ Example returns:
exports.setText = function(padID, text, callback) exports.setText = function(padID, text, callback)
{ {
// text is required // text is required
if(typeof text != "string") if (typeof text != "string") {
{
callback(new customError("text is no string", "apierror")); callback(new customError("text is no string", "apierror"));
return; return;
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// set the text // set the text
@ -304,15 +285,13 @@ Example returns:
exports.appendText = function(padID, text, callback) exports.appendText = function(padID, text, callback)
{ {
// text is required // text is required
if(typeof text != "string") if (typeof text != "string") {
{
callback(new customError("text is no string", "apierror")); callback(new customError("text is no string", "apierror"));
return; return;
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
pad.appendText(text); pad.appendText(text);
@ -322,8 +301,6 @@ exports.appendText = function(padID, text, callback)
}); });
}; };
/** /**
getHTML(padID, [rev]) returns the html of a pad getHTML(padID, [rev]) returns the html of a pad
@ -334,10 +311,8 @@ Example returns:
*/ */
exports.getHTML = function(padID, rev, callback) exports.getHTML = function(padID, rev, callback)
{ {
if (rev !== undefined && typeof rev != "number") if (rev !== undefined && typeof rev != "number") {
{ if (isNaN(parseInt(rev))) {
if (isNaN(parseInt(rev)))
{
callback(new customError("rev is not a number", "apierror")); callback(new customError("rev is not a number", "apierror"));
return; return;
} }
@ -345,35 +320,29 @@ exports.getHTML = function(padID, rev, callback)
rev = parseInt(rev); rev = parseInt(rev);
} }
if(rev !== undefined && rev < 0) if (rev !== undefined && rev < 0) {
{
callback(new customError("rev is a negative number", "apierror")); callback(new customError("rev is a negative number", "apierror"));
return; return;
} }
if(rev !== undefined && !is_int(rev)) if (rev !== undefined && !is_int(rev)) {
{
callback(new customError("rev is a float value", "apierror")); callback(new customError("rev is a float value", "apierror"));
return; return;
} }
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// the client asked for a special revision // the client asked for a special revision
if(rev !== undefined) if (rev !== undefined) {
{
// check if this is a valid revision // check if this is a valid revision
if(rev > pad.getHeadRevisionNumber()) if (rev > pad.getHeadRevisionNumber()) {
{
callback(new customError("rev is higher than the head revision of the pad", "apierror")); callback(new customError("rev is higher than the head revision of the pad", "apierror"));
return; return;
} }
// get the html of this revision // get the html of this revision
exportHtml.getPadHTML(pad, rev, function(err, html) exportHtml.getPadHTML(pad, rev, function(err, html) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
html = "<!DOCTYPE HTML><html><body>" +html; // adds HTML head html = "<!DOCTYPE HTML><html><body>" +html; // adds HTML head
html += "</body></html>"; html += "</body></html>";
@ -385,8 +354,7 @@ exports.getHTML = function(padID, rev, callback)
} }
// the client wants the latest text, lets return it to him // the client wants the latest text, lets return it to him
exportHtml.getPadHTML(pad, undefined, function (err, html) exportHtml.getPadHTML(pad, undefined, function(err, html) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
html = "<!DOCTYPE HTML><html><body>" +html; // adds HTML head html = "<!DOCTYPE HTML><html><body>" +html; // adds HTML head
html += "</body></html>"; html += "</body></html>";
@ -407,15 +375,13 @@ Example returns:
exports.setHTML = function(padID, html, callback) exports.setHTML = function(padID, html, callback)
{ {
// html is required // html is required
if(typeof html != "string") if (typeof html != "string") {
{
callback(new customError("html is no string", "apierror")); callback(new customError("html is no string", "apierror"));
return; return;
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// add a new changeset with the new html to the pad // add a new changeset with the new html to the pad
@ -449,53 +415,45 @@ Example returns:
*/ */
exports.getChatHistory = function(padID, start, end, callback) exports.getChatHistory = function(padID, start, end, callback)
{ {
if(start && end) if (start && end) {
{ if (start < 0) {
if(start < 0)
{
callback(new customError("start is below zero", "apierror")); callback(new customError("start is below zero", "apierror"));
return; return;
} }
if(end < 0) if (end < 0) {
{
callback(new customError("end is below zero", "apierror")); callback(new customError("end is below zero", "apierror"));
return; return;
} }
if(start > end) if (start > end) {
{
callback(new customError("start is higher than end", "apierror")); callback(new customError("start is higher than end", "apierror"));
return; return;
} }
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
var chatHead = pad.chatHead; var chatHead = pad.chatHead;
// fall back to getting the whole chat-history if a parameter is missing // fall back to getting the whole chat-history if a parameter is missing
if(!start || !end) if (!start || !end) {
{
start = 0; start = 0;
end = pad.chatHead; end = pad.chatHead;
} }
if(start > chatHead) if (start > chatHead) {
{
callback(new customError("start is higher than the current chatHead", "apierror")); callback(new customError("start is higher than the current chatHead", "apierror"));
return; return;
} }
if(end > chatHead) if (end > chatHead) {
{
callback(new customError("end is higher than the current chatHead", "apierror")); callback(new customError("end is higher than the current chatHead", "apierror"));
return; return;
} }
// the the whole message-log and return it to the client // the the whole message-log and return it to the client
pad.getChatMessages(start, end, pad.getChatMessages(start, end,
function(err, msgs) function(err, msgs) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {messages: msgs}); callback(null, {messages: msgs});
}); });
@ -513,15 +471,13 @@ Example returns:
exports.appendChatMessage = function(padID, text, authorID, time, callback) exports.appendChatMessage = function(padID, text, authorID, time, callback)
{ {
// text is required // text is required
if(typeof text != "string") if (typeof text != "string") {
{
callback(new customError("text is no string", "apierror")); callback(new customError("text is no string", "apierror"));
return; return;
} }
// if time is not an integer value // if time is not an integer value
if(time === undefined || !is_int(time)) if (time === undefined || !is_int(time)) {
{
// set time to current timestamp // set time to current timestamp
time = Date.now(); time = Date.now();
} }
@ -547,8 +503,7 @@ Example returns:
exports.getRevisionsCount = function(padID, callback) exports.getRevisionsCount = function(padID, callback)
{ {
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {revisions: pad.getHeadRevisionNumber()}); callback(null, {revisions: pad.getHeadRevisionNumber()});
@ -566,8 +521,7 @@ Example returns:
exports.getSavedRevisionsCount = function(padID, callback) exports.getSavedRevisionsCount = function(padID, callback)
{ {
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {savedRevisions: pad.getSavedRevisionsNumber()}); callback(null, {savedRevisions: pad.getSavedRevisionsNumber()});
@ -585,8 +539,7 @@ Example returns:
exports.listSavedRevisions = function(padID, callback) exports.listSavedRevisions = function(padID, callback)
{ {
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {savedRevisions: pad.getSavedRevisionsList()}); callback(null, {savedRevisions: pad.getSavedRevisionsList()});
@ -604,11 +557,9 @@ Example returns:
exports.saveRevision = function(padID, rev, callback) exports.saveRevision = function(padID, rev, callback)
{ {
// check if rev is a number // check if rev is a number
if(rev !== undefined && typeof rev != "number") if (rev !== undefined && typeof rev != "number") {
{
// try to parse the number // try to parse the number
if(isNaN(parseInt(rev))) if (isNaN(parseInt(rev))) {
{
callback(new customError("rev is not a number", "apierror")); callback(new customError("rev is not a number", "apierror"));
return; return;
} }
@ -616,31 +567,26 @@ exports.saveRevision = function(padID, rev, callback)
rev = parseInt(rev); rev = parseInt(rev);
} }
//ensure this is not a negativ number // ensure this is not a negative number
if(rev !== undefined && rev < 0) if (rev !== undefined && rev < 0) {
{
callback(new customError("rev is a negativ number", "apierror")); callback(new customError("rev is a negativ number", "apierror"));
return; return;
} }
// ensure this is not a float value // ensure this is not a float value
if(rev !== undefined && !is_int(rev)) if (rev !== undefined && !is_int(rev)) {
{
callback(new customError("rev is a float value", "apierror")); callback(new customError("rev is a float value", "apierror"));
return; return;
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// the client asked for a special revision // the client asked for a special revision
if(rev !== undefined) if (rev !== undefined) {
{
// check if this is a valid revision // check if this is a valid revision
if(rev > pad.getHeadRevisionNumber()) if (rev > pad.getHeadRevisionNumber()) {
{
callback(new customError("rev is higher than the head revision of the pad", "apierror")); callback(new customError("rev is higher than the head revision of the pad", "apierror"));
return; return;
} }
@ -668,9 +614,9 @@ Example returns:
exports.getLastEdited = function(padID, callback) exports.getLastEdited = function(padID, callback)
{ {
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
pad.getLastEdit(function(err, value) { pad.getLastEdit(function(err, value) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {lastEdited: value}); callback(null, {lastEdited: value});
@ -688,26 +634,22 @@ Example returns:
*/ */
exports.createPad = function(padID, text, callback) exports.createPad = function(padID, text, callback)
{ {
if (padID) {
// ensure there is no $ in the padID // ensure there is no $ in the padID
if(padID) if (padID.indexOf("$") != -1) {
{
if(padID.indexOf("$") != -1)
{
callback(new customError("createPad can't create group pads", "apierror")); callback(new customError("createPad can't create group pads", "apierror"));
return; return;
} }
// check for url special characters // check for url special characters
if(padID.match(/(\/|\?|&|#)/)) if (padID.match(/(\/|\?|&|#)/)) {
{
callback(new customError("malformed padID: Remove special characters", "apierror")); callback(new customError("malformed padID: Remove special characters", "apierror"));
return; return;
} }
} }
// create pad // create pad
getPadSafe(padID, false, text, function(err) getPadSafe(padID, false, text, function(err) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(); callback();
}); });
@ -723,8 +665,7 @@ Example returns:
*/ */
exports.deletePad = function(padID, callback) exports.deletePad = function(padID, callback)
{ {
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
pad.remove(callback); pad.remove(callback);
@ -741,11 +682,9 @@ exports.deletePad = function(padID, callback)
exports.restoreRevision = function(padID, rev, callback) exports.restoreRevision = function(padID, rev, callback)
{ {
// check if rev is a number // check if rev is a number
if (rev !== undefined && typeof rev != "number") if (rev !== undefined && typeof rev != "number") {
{
// try to parse the number // try to parse the number
if (isNaN(parseInt(rev))) if (isNaN(parseInt(rev))) {
{
callback(new customError("rev is not a number", "apierror")); callback(new customError("rev is not a number", "apierror"));
return; return;
} }
@ -753,51 +692,43 @@ exports.restoreRevision = function (padID, rev, callback)
rev = parseInt(rev); rev = parseInt(rev);
} }
//ensure this is not a negativ number // ensure this is not a negative number
if (rev !== undefined && rev < 0) if (rev !== undefined && rev < 0) {
{
callback(new customError("rev is a negativ number", "apierror")); callback(new customError("rev is a negativ number", "apierror"));
return; return;
} }
// ensure this is not a float value // ensure this is not a float value
if (rev !== undefined && !is_int(rev)) if (rev !== undefined && !is_int(rev)) {
{
callback(new customError("rev is a float value", "apierror")); callback(new customError("rev is a float value", "apierror"));
return; return;
} }
// get the pad // get the pad
getPadSafe(padID, true, function (err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// check if this is a valid revision // check if this is a valid revision
if (rev > pad.getHeadRevisionNumber()) if (rev > pad.getHeadRevisionNumber()) {
{
callback(new customError("rev is higher than the head revision of the pad", "apierror")); callback(new customError("rev is higher than the head revision of the pad", "apierror"));
return; return;
} }
pad.getInternalRevisionAText(rev, function (err, atext) pad.getInternalRevisionAText(rev, function(err, atext) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
var oldText = pad.text(); var oldText = pad.text();
atext.text += "\n"; atext.text += "\n";
function eachAttribRun(attribs, func) function eachAttribRun(attribs, func) {
{
var attribsIter = Changeset.opIterator(attribs); var attribsIter = Changeset.opIterator(attribs);
var textIndex = 0; var textIndex = 0;
var newTextStart = 0; var newTextStart = 0;
var newTextEnd = atext.text.length; var newTextEnd = atext.text.length;
while (attribsIter.hasNext()) while (attribsIter.hasNext()) {
{
var op = attribsIter.next(); var op = attribsIter.next();
var nextIndex = textIndex + op.chars; var nextIndex = textIndex + op.chars;
if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) {
{
func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs);
} }
textIndex = nextIndex; textIndex = nextIndex;
@ -808,17 +739,14 @@ exports.restoreRevision = function (padID, rev, callback)
var builder = Changeset.builder(oldText.length); var builder = Changeset.builder(oldText.length);
// assemble each line into the builder // assemble each line into the builder
eachAttribRun(atext.attribs, function (start, end, attribs) eachAttribRun(atext.attribs, function(start, end, attribs) {
{
builder.insert(atext.text.substring(start, end), attribs); builder.insert(atext.text.substring(start, end), attribs);
}); });
var lastNewlinePos = oldText.lastIndexOf('\n'); var lastNewlinePos = oldText.lastIndexOf('\n');
if (lastNewlinePos < 0) if (lastNewlinePos < 0) {
{
builder.remove(oldText.length - 1, 0); builder.remove(oldText.length - 1, 0);
} else } else {
{
builder.remove(lastNewlinePos, oldText.match(/\n/g).length - 1); builder.remove(lastNewlinePos, oldText.match(/\n/g).length - 1);
builder.remove(oldText.length - lastNewlinePos - 1, 0); builder.remove(oldText.length - lastNewlinePos - 1, 0);
} }
@ -827,10 +755,9 @@ exports.restoreRevision = function (padID, rev, callback)
// append the changeset // append the changeset
pad.appendRevision(changeset); pad.appendRevision(changeset);
//
padMessageHandler.updatePadClients(pad, function () // update the clients on the pad
{ padMessageHandler.updatePadClients(pad, function() {});
});
callback(null, null); callback(null, null);
}); });
@ -848,8 +775,7 @@ Example returns:
*/ */
exports.copyPad = function(sourceID, destinationID, force, callback) exports.copyPad = function(sourceID, destinationID, force, callback)
{ {
getPadSafe(sourceID, true, function(err, pad) getPadSafe(sourceID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
pad.copy(destinationID, force, callback); pad.copy(destinationID, force, callback);
@ -867,8 +793,7 @@ Example returns:
*/ */
exports.movePad = function(sourceID, destinationID, force, callback) exports.movePad = function(sourceID, destinationID, force, callback)
{ {
getPadSafe(sourceID, true, function(err, pad) getPadSafe(sourceID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
pad.copy(destinationID, force, function(err) { pad.copy(destinationID, force, function(err) {
@ -888,13 +813,11 @@ Example returns:
exports.getReadOnlyID = function(padID, callback) exports.getReadOnlyID = function(padID, callback)
{ {
// we don't need the pad object, but this function does all the security stuff for us // we don't need the pad object, but this function does all the security stuff for us
getPadSafe(padID, true, function(err) getPadSafe(padID, true, function(err) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// get the readonlyId // get the readonlyId
readOnlyManager.getReadOnlyId(padID, function(err, readOnlyId) readOnlyManager.getReadOnlyId(padID, function(err, readOnlyId) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {readOnlyID: readOnlyId}); callback(null, {readOnlyID: readOnlyId});
}); });
@ -912,12 +835,10 @@ Example returns:
exports.getPadID = function(roID, callback) exports.getPadID = function(roID, callback)
{ {
// get the PadId // get the PadId
readOnlyManager.getPadId(roID, function(err, retrievedPadID) readOnlyManager.getPadId(roID, function(err, retrievedPadID) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
if(retrievedPadID == null) if (retrievedPadID == null) {
{
callback(new customError("padID does not exist", "apierror")); callback(new customError("padID does not exist", "apierror"));
return; return;
} }
@ -937,15 +858,13 @@ Example returns:
exports.setPublicStatus = function(padID, publicStatus, callback) exports.setPublicStatus = function(padID, publicStatus, callback)
{ {
// ensure this is a group pad // ensure this is a group pad
if(padID && padID.indexOf("$") == -1) if (padID && padID.indexOf("$") == -1) {
{
callback(new customError("You can only get/set the publicStatus of pads that belong to a group", "apierror")); callback(new customError("You can only get/set the publicStatus of pads that belong to a group", "apierror"));
return; return;
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// convert string to boolean // convert string to boolean
@ -970,15 +889,13 @@ Example returns:
exports.getPublicStatus = function(padID, callback) exports.getPublicStatus = function(padID, callback)
{ {
// ensure this is a group pad // ensure this is a group pad
if(padID && padID.indexOf("$") == -1) if (padID && padID.indexOf("$") == -1) {
{
callback(new customError("You can only get/set the publicStatus of pads that belong to a group", "apierror")); callback(new customError("You can only get/set the publicStatus of pads that belong to a group", "apierror"));
return; return;
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {publicStatus: pad.getPublicStatus()}); callback(null, {publicStatus: pad.getPublicStatus()});
@ -996,15 +913,13 @@ Example returns:
exports.setPassword = function(padID, password, callback) exports.setPassword = function(padID, password, callback)
{ {
// ensure this is a group pad // ensure this is a group pad
if(padID && padID.indexOf("$") == -1) if (padID && padID.indexOf("$") == -1) {
{
callback(new customError("You can only get/set the password of pads that belong to a group", "apierror")); callback(new customError("You can only get/set the password of pads that belong to a group", "apierror"));
return; return;
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// set the password // set the password
@ -1025,15 +940,13 @@ Example returns:
exports.isPasswordProtected = function(padID, callback) exports.isPasswordProtected = function(padID, callback)
{ {
// ensure this is a group pad // ensure this is a group pad
if(padID && padID.indexOf("$") == -1) if (padID && padID.indexOf("$") == -1) {
{
callback(new customError("You can only get/set the password of pads that belong to a group", "apierror")); callback(new customError("You can only get/set the password of pads that belong to a group", "apierror"));
return; return;
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {isPasswordProtected: pad.isPasswordProtected()}); callback(null, {isPasswordProtected: pad.isPasswordProtected()});
@ -1051,8 +964,7 @@ Example returns:
exports.listAuthorsOfPad = function(padID, callback) exports.listAuthorsOfPad = function(padID, callback)
{ {
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {authorIDs: pad.getAllAuthors()}); callback(null, {authorIDs: pad.getAllAuthors()});
@ -1116,9 +1028,9 @@ Example returns:
exports.getChatHead = function(padID, callback) exports.getChatHead = function(padID, callback)
{ {
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {chatHead: pad.chatHead}); callback(null, {chatHead: pad.chatHead});
}); });
} }
@ -1132,12 +1044,10 @@ Example returns:
{"code":4,"message":"no or wrong API Key","data":null} {"code":4,"message":"no or wrong API Key","data":null}
*/ */
exports.createDiffHTML = function(padID, startRev, endRev, callback) { exports.createDiffHTML = function(padID, startRev, endRev, callback) {
//check if rev is a number // check if startRev is a number
if(startRev !== undefined && typeof startRev != "number") if (startRev !== undefined && typeof startRev != "number") {
{
// try to parse the number // try to parse the number
if(isNaN(parseInt(startRev))) if (isNaN(parseInt(startRev))) {
{
callback({stop: "startRev is not a number"}); callback({stop: "startRev is not a number"});
return; return;
} }
@ -1145,12 +1055,10 @@ exports.createDiffHTML = function(padID, startRev, endRev, callback){
startRev = parseInt(startRev, 10); startRev = parseInt(startRev, 10);
} }
//check if rev is a number // check if endRev is a number
if(endRev !== undefined && typeof endRev != "number") if (endRev !== undefined && typeof endRev != "number") {
{
// try to parse the number // try to parse the number
if(isNaN(parseInt(endRev))) if (isNaN(parseInt(endRev))) {
{
callback({stop: "endRev is not a number"}); callback({stop: "endRev is not a number"});
return; return;
} }
@ -1159,8 +1067,7 @@ exports.createDiffHTML = function(padID, startRev, endRev, callback){
} }
// get the pad // get the pad
getPadSafe(padID, true, function(err, pad) getPadSafe(padID, true, function(err, pad) {
{
if (err) { if (err) {
return callback(err); return callback(err);
} }
@ -1212,44 +1119,35 @@ function is_int(value)
// gets a pad safe // gets a pad safe
function getPadSafe(padID, shouldExist, text, callback) function getPadSafe(padID, shouldExist, text, callback)
{ {
if(typeof text == "function") if (typeof text == "function") {
{
callback = text; callback = text;
text = null; text = null;
} }
// check if padID is a string // check if padID is a string
if(typeof padID != "string") if (typeof padID != "string") {
{
callback(new customError("padID is not a string", "apierror")); callback(new customError("padID is not a string", "apierror"));
return; return;
} }
// check if the padID maches the requirements // check if the padID maches the requirements
if(!padManager.isValidPadId(padID)) if (!padManager.isValidPadId(padID)) {
{
callback(new customError("padID did not match requirements", "apierror")); callback(new customError("padID did not match requirements", "apierror"));
return; return;
} }
// check if the pad exists // check if the pad exists
padManager.doesPadExists(padID, function(err, exists) padManager.doesPadExists(padID, function(err, exists) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// does not exist, but should // does not exist, but should
if(exists == false && shouldExist == true) if (exists == false && shouldExist == true) {
{
callback(new customError("padID does not exist", "apierror")); callback(new customError("padID does not exist", "apierror"));
} } else if (exists == true && shouldExist == false) {
//does exists, but shouldn't // does exist, but shouldn't
else if(exists == true && shouldExist == false)
{
callback(new customError("padID does already exist", "apierror")); callback(new customError("padID does already exist", "apierror"));
} } else {
// pad exists, let's get it // pad exists, let's get it
else
{
padManager.getPad(padID, text, callback); padManager.getPad(padID, text, callback);
} }
}); });

View file

@ -18,14 +18,22 @@
* limitations under the License. * limitations under the License.
*/ */
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var db = require("./DB").db; var db = require("./DB").db;
var customError = require("../utils/customError"); var customError = require("../utils/customError");
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
exports.getColorPalette = function() { exports.getColorPalette = function() {
return ["#ffc7c7", "#fff1c7", "#e3ffc7", "#c7ffd5", "#c7ffff", "#c7d5ff", "#e3c7ff", "#ffc7f1", "#ffa8a8", "#ffe699", "#cfff9e", "#99ffb3", "#a3ffff", "#99b3ff", "#cc99ff", "#ff99e5", "#e7b1b1", "#e9dcAf", "#cde9af", "#bfedcc", "#b1e7e7", "#c3cdee", "#d2b8ea", "#eec3e6", "#e9cece", "#e7e0ca", "#d3e5c7", "#bce1c5", "#c1e2e2", "#c1c9e2", "#cfc1e2", "#e0bdd9", "#baded3", "#a0f8eb", "#b1e7e0", "#c3c8e4", "#cec5e2", "#b1d5e7", "#cda8f0", "#f0f0a8", "#f2f2a6", "#f5a8eb", "#c5f9a9", "#ececbb", "#e7c4bc", "#daf0b2", "#b0a0fd", "#bce2e7", "#cce2bb", "#ec9afe", "#edabbd", "#aeaeea", "#c4e7b1", "#d722bb", "#f3a5e7", "#ffa8a8", "#d8c0c5", "#eaaedd", "#adc6eb", "#bedad1", "#dee9af", "#e9afc2", "#f8d2a0", "#b3b3e6"]; return [
"#ffc7c7", "#fff1c7", "#e3ffc7", "#c7ffd5", "#c7ffff", "#c7d5ff", "#e3c7ff", "#ffc7f1",
"#ffa8a8", "#ffe699", "#cfff9e", "#99ffb3", "#a3ffff", "#99b3ff", "#cc99ff", "#ff99e5",
"#e7b1b1", "#e9dcAf", "#cde9af", "#bfedcc", "#b1e7e7", "#c3cdee", "#d2b8ea", "#eec3e6",
"#e9cece", "#e7e0ca", "#d3e5c7", "#bce1c5", "#c1e2e2", "#c1c9e2", "#cfc1e2", "#e0bdd9",
"#baded3", "#a0f8eb", "#b1e7e0", "#c3c8e4", "#cec5e2", "#b1d5e7", "#cda8f0", "#f0f0a8",
"#f2f2a6", "#f5a8eb", "#c5f9a9", "#ececbb", "#e7c4bc", "#daf0b2", "#b0a0fd", "#bce2e7",
"#cce2bb", "#ec9afe", "#edabbd", "#aeaeea", "#c4e7b1", "#d722bb", "#f3a5e7", "#ffa8a8",
"#d8c0c5", "#eaaedd", "#adc6eb", "#bedad1", "#dee9af", "#e9afc2", "#f8d2a0", "#b3b3e6"
];
}; };
/** /**
@ -34,9 +42,9 @@ exports.getColorPalette = function(){
exports.doesAuthorExists = function(authorID, callback) exports.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) 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);
}); });
} }
@ -48,9 +56,9 @@ exports.doesAuthorExists = function (authorID, callback)
*/ */
exports.getAuthor4Token = function(token, callback) exports.getAuthor4Token = function(token, callback)
{ {
mapAuthorWithDBKey("token2author", token, function(err, author) 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);
}); });
@ -64,13 +72,13 @@ exports.getAuthor4Token = function (token, callback)
*/ */
exports.createAuthorIfNotExistsFor = function(authorMapper, name, callback) exports.createAuthorIfNotExistsFor = function(authorMapper, name, callback)
{ {
mapAuthorWithDBKey("mapper2author", authorMapper, function(err, author) mapAuthorWithDBKey("mapper2author", authorMapper, function(err, author) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
if (name) {
// set the name of this author // set the name of this author
if(name)
exports.setAuthorName(author.authorID, name); exports.setAuthorName(author.authorID, name);
}
// return the authorID // return the authorID
callback(null, author); callback(null, author);
@ -87,15 +95,12 @@ exports.createAuthorIfNotExistsFor = function (authorMapper, name, callback)
function mapAuthorWithDBKey (mapperkey, mapper, callback) function mapAuthorWithDBKey (mapperkey, mapper, callback)
{ {
// try to map to an author // try to map to an author
db.get(mapperkey + ":" + mapper, function (err, author) db.get(mapperkey + ":" + mapper, function(err, author) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
if (author == null) {
// there is no author with this mapper, so create one // there is no author with this mapper, so create one
if(author == null) exports.createAuthor(null, function(err, author) {
{
exports.createAuthor(null, function(err, author)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// create the token2author relation // create the token2author relation
@ -108,7 +113,7 @@ function mapAuthorWithDBKey (mapperkey, mapper, callback)
return; return;
} }
//there is a author with this mapper // there is an author with this mapper
// update the timestamp of this author // update the timestamp of this author
db.setSub("globalAuthor:" + author, ["timestamp"], Date.now()); db.setSub("globalAuthor:" + author, ["timestamp"], Date.now());
@ -127,7 +132,11 @@ exports.createAuthor = function(name, callback)
var author = "a." + randomString(16); var author = "a." + randomString(16);
// create the globalAuthors db entry // create the globalAuthors db entry
var authorObj = {"colorId" : Math.floor(Math.random()*(exports.getColorPalette().length)), "name": name, "timestamp": Date.now()}; var authorObj = {
"colorId": Math.floor(Math.random() * (exports.getColorPalette().length)),
"name": name,
"timestamp": Date.now()
};
// set the global author db entry // set the global author db entry
db.set("globalAuthor:" + author, authorObj); db.set("globalAuthor:" + author, authorObj);
@ -145,8 +154,6 @@ exports.getAuthor = function (author, callback)
db.get("globalAuthor:" + author, callback); 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
@ -200,27 +207,27 @@ exports.listPadsOfAuthor = function (authorID, callback)
* (1) When the author is added to a pad, the author object is also updated * (1) When the author is added to a pad, the author object is also updated
* (2) When a pad is deleted, each author of that pad is also updated * (2) When a pad is deleted, each author of that pad is also updated
*/ */
// get the globalAuthor // get the globalAuthor
db.get("globalAuthor:" + authorID, function(err, author) db.get("globalAuthor:" + authorID, function(err, author) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
//author does not exists if (author == null) {
if(author == null) // author does not exist
{ callback(new customError("authorID does not exist", "apierror"));
callback(new customError("authorID does not exist","apierror"))
return; return;
} }
// everything is fine, return the pad IDs // everything is fine, return the pad IDs
var pads = []; var pads = [];
if(author.padIDs != null)
{ if (author.padIDs != null) {
for (var padId in author.padIDs) for (var padId in author.padIDs) {
{
pads.push(padId); pads.push(padId);
} }
} }
callback(null, {padIDs: pads}); callback(null, {padIDs: pads});
}); });
} }
@ -233,14 +240,12 @@ exports.listPadsOfAuthor = function (authorID, callback)
exports.addPad = function(authorID, padID) exports.addPad = function(authorID, padID)
{ {
// get the entry // get the entry
db.get("globalAuthor:" + authorID, function(err, author) db.get("globalAuthor:" + authorID, function(err, author) {
{
if (ERR(err)) return; if (ERR(err)) return;
if (author == null) return; if (author == null) return;
if (author.padIDs == null) {
// the entry doesn't exist so far, let's create it // the entry doesn't exist so far, let's create it
if(author.padIDs == null)
{
author.padIDs = {}; author.padIDs = {};
} }
@ -259,13 +264,11 @@ exports.addPad = function (authorID, padID)
*/ */
exports.removePad = function(authorID, padID) exports.removePad = function(authorID, padID)
{ {
db.get("globalAuthor:" + authorID, function (err, author) db.get("globalAuthor:" + authorID, function(err, author) {
{
if (ERR(err)) return; if (ERR(err)) return;
if (author == null) return; if (author == null) return;
if(author.padIDs != null) if (author.padIDs != null) {
{
// remove pad from author // remove pad from author
delete author.padIDs[padID]; delete author.padIDs[padID];
db.set("globalAuthor:" + authorID, author); db.set("globalAuthor:" + authorID, author);

View file

@ -35,21 +35,16 @@ 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(callback) {
{
// initalize the database async // initalize the database async
db.init(function(err) db.init(function(err) {
{ if (err) {
// 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)
{
console.error("ERROR: Problem while initalizing the database"); console.error("ERROR: Problem while initalizing the database");
console.error(err.stack ? err.stack : err); console.error(err.stack ? err.stack : err);
process.exit(1); process.exit(1);
} } else {
// everything ok // everything ok
else
{
exports.db = db; exports.db = db;
callback(null); callback(null);
} }

View file

@ -18,7 +18,6 @@
* limitations under the License. * limitations under the License.
*/ */
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var customError = require("../utils/customError"); var customError = require("../utils/customError");
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString; var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
@ -31,9 +30,10 @@ exports.listAllGroups = function(callback) {
db.get("groups", function (err, groups) { db.get("groups", function (err, groups) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// there are no groups
if (groups == null) { if (groups == null) {
// there are no groups
callback(null, {groupIDs: []}); callback(null, {groupIDs: []});
return; return;
} }
@ -51,16 +51,13 @@ exports.deleteGroup = function(groupID, callback)
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) db.get("group:" + groupID, function (err, _group) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
if (_group == null) {
// group does not exist // group does not exist
if(_group == null)
{
callback(new customError("groupID does not exist", "apierror")); callback(new customError("groupID does not exist", "apierror"));
return; return;
} }
@ -70,33 +67,30 @@ exports.deleteGroup = function(groupID, callback)
callback(); callback();
}); });
}, },
//iterate trough all pads of this groups and delete them
function(callback) // iterate through all pads of this group and delete them
{ function(callback) {
// collect all padIDs in an array, that allows us to use async.forEach // collect all padIDs in an array, that allows us to use async.forEach
var padIDs = []; var padIDs = [];
for(var i in group.pads) for(var i in group.pads) {
{
padIDs.push(i); padIDs.push(i);
} }
//loop trough all pads and delete them // loop through all pads and delete them
async.forEach(padIDs, function(padID, callback) async.forEach(padIDs, function(padID, callback) {
{ padManager.getPad(padID, function(err, pad) {
padManager.getPad(padID, function(err, pad)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
pad.remove(callback); pad.remove(callback);
}); });
}, callback); }, callback);
}, },
//iterate trough group2sessions and delete all sessions
// iterate through group2sessions and delete all sessions
function(callback) function(callback)
{ {
// try to get the group entry // try to get the group entry
db.get("group2sessions:" + groupID, function (err, group2sessions) db.get("group2sessions:" + groupID, function (err, group2sessions) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// skip if there is no group2sessions entry // skip if there is no group2sessions entry
@ -104,41 +98,41 @@ exports.deleteGroup = function(groupID, callback)
// collect all sessions in an array, that allows us to use async.forEach // collect all sessions in an array, that allows us to use async.forEach
var sessions = []; var sessions = [];
for(var i in group2sessions.sessionsIDs) for (var i in group2sessions.sessionsIDs) {
{
sessions.push(i); sessions.push(i);
} }
//loop trough all sessions and delete them // loop through all sessions and delete them
async.forEach(sessions, function(session, callback) async.forEach(sessions, function(session, callback) {
{
sessionManager.deleteSession(session, callback); sessionManager.deleteSession(session, callback);
}, callback); }, callback);
}); });
}, },
// remove group and group2sessions entry // remove group and group2sessions entry
function(callback) function(callback) {
{
db.remove("group2sessions:" + groupID); db.remove("group2sessions:" + groupID);
db.remove("group:" + groupID); db.remove("group:" + groupID);
callback(); callback();
}, },
// unlist the group // unlist the group
function(callback) function(callback) {
{
exports.listAllGroups(function(err, groups) { exports.listAllGroups(function(err, groups) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
groups = groups? groups.groupIDs : []; groups = groups? groups.groupIDs : [];
// it's not listed
if (groups.indexOf(groupID) == -1) { if (groups.indexOf(groupID) == -1) {
// it's not listed
callback(); callback();
return; return;
} }
// remove from the list
groups.splice(groups.indexOf(groupID), 1); groups.splice(groups.indexOf(groupID), 1);
// store empty groupe list // store empty group list
if (groups.length == 0) { if (groups.length == 0) {
db.set("groups", {}); db.set("groups", {});
callback(); callback();
@ -150,14 +144,15 @@ exports.deleteGroup = function(groupID, callback)
async.forEach(groups, function(group, cb) { async.forEach(groups, function(group, cb) {
newGroups[group] = 1; newGroups[group] = 1;
cb(); cb();
},function() { },
function() {
db.set("groups", newGroups); db.set("groups", newGroups);
callback(); callback();
}); });
}); });
} }
], function(err) ],
{ function(err) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(); callback();
}); });
@ -166,8 +161,7 @@ exports.deleteGroup = function(groupID, callback)
exports.doesGroupExist = function(groupID, callback) exports.doesGroupExist = function(groupID, callback)
{ {
// try to get the group entry // try to get the group entry
db.get("group:" + groupID, function (err, group) 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);
}); });
@ -184,8 +178,8 @@ exports.createGroup = function(callback)
// list the group // list the group
exports.listAllGroups(function(err, groups) { exports.listAllGroups(function(err, groups) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
groups = groups? groups.groupIDs : [];
groups = groups? groups.groupIDs : [];
groups.push(groupID); groups.push(groupID);
// regenerate group list // regenerate group list
@ -193,7 +187,8 @@ exports.createGroup = function(callback)
async.forEach(groups, function(group, cb) { async.forEach(groups, function(group, cb) {
newGroups[group] = 1; newGroups[group] = 1;
cb(); cb();
},function() { },
function() {
db.set("groups", newGroups); db.set("groups", newGroups);
callback(null, {groupID: groupID}); callback(null, {groupID: groupID});
}); });
@ -203,18 +198,15 @@ exports.createGroup = function(callback)
exports.createGroupIfNotExistsFor = function(groupMapper, callback) exports.createGroupIfNotExistsFor = function(groupMapper, callback)
{ {
// ensure mapper is optional // ensure mapper is optional
if(typeof groupMapper != "string") if (typeof groupMapper != "string") {
{
callback(new customError("groupMapper is no string", "apierror")); callback(new customError("groupMapper is no string", "apierror"));
return; return;
} }
// try to get a group for this mapper // try to get a group for this mapper
db.get("mapper2group:"+groupMapper, function(err, groupID) db.get("mapper2group:" + groupMapper, function(err, groupID) {
{
function createGroupForMapper(cb) { function createGroupForMapper(cb) {
exports.createGroup(function(err, responseObj) exports.createGroup(function(err, responseObj) {
{
if (ERR(err, cb)) return; if (ERR(err, cb)) return;
// create the mapper entry for this group // create the mapper entry for this group
@ -226,15 +218,16 @@ exports.createGroupIfNotExistsFor = function(groupMapper, callback)
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// there is a group for this mapper
if (groupID) { if (groupID) {
// there is a group for this mapper
exports.doesGroupExist(groupID, function(err, exists) { exports.doesGroupExist(groupID, function(err, exists) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
if (exists) return callback(null, {groupID: groupID}); if (exists) return callback(null, {groupID: groupID});
// hah, the returned group doesn't exist, let's create one // hah, the returned group doesn't exist, let's create one
createGroupForMapper(callback) createGroupForMapper(callback)
}) });
return; return;
} }
@ -251,15 +244,12 @@ exports.createGroupPad = function(groupID, padName, text, callback)
async.series([ async.series([
// ensure group exists // ensure group exists
function (callback) function (callback) {
{ exports.doesGroupExist(groupID, function(err, exists) {
exports.doesGroupExist(groupID, function(err, exists)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
if (exists == false) {
// group does not exist // group does not exist
if(exists == false)
{
callback(new customError("groupID does not exist", "apierror")); callback(new customError("groupID does not exist", "apierror"));
return; return;
} }
@ -268,16 +258,14 @@ exports.createGroupPad = function(groupID, padName, text, callback)
callback(); callback();
}); });
}, },
//ensure pad does not exists
function (callback) // ensure pad doesn't exist already
{ function (callback) {
padManager.doesPadExists(padID, function(err, exists) padManager.doesPadExists(padID, function(err, exists) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
if (exists == true) {
// pad exists already // pad exists already
if(exists == true)
{
callback(new customError("padName does already exist", "apierror")); callback(new customError("padName does already exist", "apierror"));
return; return;
} }
@ -286,45 +274,44 @@ exports.createGroupPad = function(groupID, padName, text, callback)
callback(); callback();
}); });
}, },
// create the pad // create the pad
function (callback) function (callback) {
{ padManager.getPad(padID, text, function(err) {
padManager.getPad(padID, text, function(err)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(); 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); db.setSub("group:" + groupID, ["pads", padID], 1);
callback(); callback();
} }
], function(err) ],
{ function(err) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, {padID: padID}); callback(null, {padID: padID});
}); });
} }
exports.listPads = function(groupID, callback) exports.listPads = function(groupID, callback)
{ {
exports.doesGroupExist(groupID, function(err, exists) exports.doesGroupExist(groupID, function(err, exists) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
//group does not exist // ensure the group exists
if(exists == false) if (exists == false) {
{
callback(new customError("groupID does not exist", "apierror")); callback(new customError("groupID does not exist", "apierror"));
return; return;
} }
// group exists, let's get the pads // group exists, let's get the pads
db.getSub("group:" + groupID, ["pads"], function(err, result) db.getSub("group:" + groupID, ["pads"], function(err, result) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
var pads = []; var pads = [];
for ( var padId in result ) { for ( var padId in result ) {
pads.push(padId); pads.push(padId);

View file

@ -33,7 +33,6 @@ exports.cleanText = function (txt) {
var Pad = function Pad(id) { var Pad = function Pad(id) {
this.atext = Changeset.makeAText("\n"); this.atext = Changeset.makeAText("\n");
this.pool = new AttributePool(); this.pool = new AttributePool();
this.head = -1; this.head = -1;
@ -74,8 +73,9 @@ Pad.prototype.getPublicStatus = function getPublicStatus() {
}; };
Pad.prototype.appendRevision = function appendRevision(aChangeset, author) { Pad.prototype.appendRevision = function appendRevision(aChangeset, author) {
if(!author) if (!author) {
author = ''; author = '';
}
var newAText = Changeset.applyToAText(aChangeset, this.atext, this.pool); var newAText = Changeset.applyToAText(aChangeset, this.atext, this.pool);
Changeset.copyAText(newAText, this.atext); Changeset.copyAText(newAText, this.atext);
@ -89,11 +89,11 @@ Pad.prototype.appendRevision = function appendRevision(aChangeset, author) {
newRevData.meta.timestamp = Date.now(); newRevData.meta.timestamp = Date.now();
// ex. getNumForAuthor // ex. getNumForAuthor
if(author != '') if (author != '') {
this.pool.putAttrib(['author', author || '']); this.pool.putAttrib(['author', author || '']);
}
if(newRev % 100 == 0) if (newRev % 100 == 0) {
{
newRevData.meta.atext = this.atext; newRevData.meta.atext = this.atext;
} }
@ -101,8 +101,9 @@ Pad.prototype.appendRevision = function appendRevision(aChangeset, author) {
this.saveToDatabase(); this.saveToDatabase();
// set the author to pad // set the author to pad
if(author) if (author) {
authorManager.addPad(author, this.id); authorManager.addPad(author, this.id);
}
if (this.head == 0) { if (this.head == 0) {
hooks.callAll("padCreate", {'pad':this, 'author': author}); hooks.callAll("padCreate", {'pad':this, 'author': author});
@ -150,10 +151,8 @@ Pad.prototype.getRevisionDate = function getRevisionDate(revNum, callback) {
Pad.prototype.getAllAuthors = function getAllAuthors() { Pad.prototype.getAllAuthors = function getAllAuthors() {
var authors = []; var authors = [];
for(var key in this.pool.numToAttrib) for(var key in this.pool.numToAttrib) {
{ if (this.pool.numToAttrib[key][0] == "author" && this.pool.numToAttrib[key][1] != "") {
if(this.pool.numToAttrib[key][0] == "author" && this.pool.numToAttrib[key][1] != "")
{
authors.push(this.pool.numToAttrib[key][1]); authors.push(this.pool.numToAttrib[key][1]);
} }
} }
@ -171,22 +170,18 @@ Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targe
// find out which changesets are needed // find out which changesets are needed
var neededChangesets = []; var neededChangesets = [];
var curRev = keyRev; var curRev = keyRev;
while (curRev < targetRev) while (curRev < targetRev) {
{
curRev++; curRev++;
neededChangesets.push(curRev); neededChangesets.push(curRev);
} }
async.series([ async.series([
// get all needed data out of the database // get all needed data out of the database
function(callback) function(callback) {
{
async.parallel([ async.parallel([
// 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) {
db.getSub("pad:"+_this.id+":revs:"+keyRev, ["meta", "atext"], function(err, _atext)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
try { try {
atext = Changeset.cloneAText(_atext); atext = Changeset.cloneAText(_atext);
@ -197,13 +192,11 @@ Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targe
callback(); callback();
}); });
}, },
// get all needed changesets // get all needed changesets
function (callback) function (callback) {
{ async.forEach(neededChangesets, function(item, callback) {
async.forEach(neededChangesets, function(item, callback) _this.getRevisionChangeset(item, function(err, changeset) {
{
_this.getRevisionChangeset(item, function(err, changeset)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
changesets[item] = changeset; changesets[item] = changeset;
callback(); callback();
@ -212,14 +205,13 @@ Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targe
} }
], callback); ], callback);
}, },
// apply all changesets to the key changeset // apply all changesets to the key changeset
function(callback) function(callback) {
{
var apool = _this.apool(); var apool = _this.apool();
var curRev = keyRev; var curRev = keyRev;
while (curRev < targetRev) while (curRev < targetRev) {
{
curRev++; curRev++;
var cs = changesets[curRev]; var cs = changesets[curRev];
try { try {
@ -231,8 +223,8 @@ Pad.prototype.getInternalRevisionAText = function getInternalRevisionAText(targe
callback(null); callback(null);
} }
], function(err) ],
{ function(err) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, atext); callback(null, atext);
}); });
@ -252,12 +244,14 @@ Pad.prototype.getAllAuthorColors = function getAllAuthorColors(callback){
if (err) { if (err) {
return callback(err); return callback(err);
} }
// colorId might be a hex color or an number out of the palette // colorId might be a hex color or an number out of the palette
returnTable[author] = colorPalette[colorId] || colorId; returnTable[author] = colorPalette[colorId] || colorId;
callback(); callback();
}); });
}, function(err){ },
function(err) {
callback(err, returnTable); callback(err, returnTable);
}); });
}; };
@ -266,14 +260,17 @@ Pad.prototype.getValidRevisionRange = function getValidRevisionRange(startRev, e
startRev = parseInt(startRev, 10); startRev = parseInt(startRev, 10);
var head = this.getHeadRevisionNumber(); var head = this.getHeadRevisionNumber();
endRev = endRev ? parseInt(endRev, 10) : head; endRev = endRev ? parseInt(endRev, 10) : head;
if (isNaN(startRev) || startRev < 0 || startRev > head) { if (isNaN(startRev) || startRev < 0 || startRev > head) {
startRev = null; startRev = null;
} }
if (isNaN(endRev) || endRev < startRev) { if (isNaN(endRev) || endRev < startRev) {
endRev = null; endRev = null;
} else if (endRev > head) { } else if (endRev > head) {
endRev = head; endRev = head;
} }
if (startRev !== null && endRev !== null) { if (startRev !== null && endRev !== null) {
return { startRev: startRev , endRev: endRev } return { startRev: startRev , endRev: endRev }
} }
@ -334,35 +331,31 @@ Pad.prototype.getChatMessage = function getChatMessage(entryNum, callback) {
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) {
db.get("pad:"+_this.id+":chat:"+entryNum, function(err, _entry)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
entry = _entry; entry = _entry;
callback(); callback();
}); });
}, },
// add the authorName // add the authorName
function(callback) function(callback) {
{
// this chat message doesn't exist, return null // this chat message doesn't exist, return null
if(entry == null) if (entry == null) {
{
callback(); callback();
return; return;
} }
// get the authorName // get the authorName
authorManager.getAuthorName(entry.userId, function(err, authorName) authorManager.getAuthorName(entry.userId, function(err, authorName) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
entry.userName = authorName; entry.userName = authorName;
callback(); callback();
}); });
} }
], function(err) ],
{ function(err) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, entry); callback(null, entry);
}); });
@ -372,8 +365,7 @@ Pad.prototype.getChatMessages = function getChatMessages(start, end, callback) {
// collect the numbers of chat entries and in which order we need them // collect the numbers of chat entries and in which order we need them
var neededEntries = []; var neededEntries = [];
var order = 0; var order = 0;
for(var i=start;i<=end; i++) for (var i = start; i <= end; i++) {
{
neededEntries.push({ entryNum: i, order: order }); neededEntries.push({ entryNum: i, order: order });
order++; order++;
} }
@ -382,29 +374,27 @@ Pad.prototype.getChatMessages = function getChatMessages(start, end, callback) {
// get all entries out of the database // get all entries out of the database
var entries = []; var entries = [];
async.forEach(neededEntries, function(entryObject, callback) async.forEach(neededEntries, function(entryObject, callback) {
{ _this.getChatMessage(entryObject.entryNum, function(err, entry) {
_this.getChatMessage(entryObject.entryNum, function(err, entry)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
entries[entryObject.order] = entry; entries[entryObject.order] = entry;
callback(); callback();
}); });
}, function(err) },
{ function(err) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// sort out broken chat entries // sort out broken chat entries
//it looks like in happend in the past that the chat head was // it looks like in happened in the past that the chat head was
// incremented, but the chat message wasn't added // incremented, but the chat message wasn't added
var cleanedEntries = []; var cleanedEntries = [];
for(var i=0;i<entries.length;i++) for (var i=0; i < entries.length; i++) {
{ 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 " + _this.id);
} }
}
callback(null, cleanedEntries); callback(null, cleanedEntries);
}); });
@ -414,20 +404,17 @@ Pad.prototype.init = function init(text, callback) {
var _this = this; var _this = this;
// replace text with default text if text isn't set // replace text with default text if text isn't set
if(text == null) if (text == null) {
{
text = settings.defaultPadText; text = settings.defaultPadText;
} }
// try to load the pad // try to load the pad
db.get("pad:"+this.id, function(err, value) 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) {
{ // copy all attr. To a transfrom via fromJsonable if necessary
//copy all attr. To a transfrom via fromJsonable if necassary
for (var attr in value) { for (var attr in value) {
if (jsonableList.indexOf(attr) !== -1) { if (jsonableList.indexOf(attr) !== -1) {
_this[attr] = _this[attr].fromJsonable(value[attr]); _this[attr] = _this[attr].fromJsonable(value[attr]);
@ -435,10 +422,8 @@ Pad.prototype.init = function init(text, callback) {
_this[attr] = value[attr]; _this[attr] = value[attr];
} }
} }
} } else {
// this pad doesn't exist, so create it // this pad doesn't exist, so create it
else
{
var firstChangeset = Changeset.makeSplice("\n", 0, 0, exports.cleanText(text)); var firstChangeset = Changeset.makeSplice("\n", 0, 0, exports.cleanText(text));
_this.appendRevision(firstChangeset, ''); _this.appendRevision(firstChangeset, '');
@ -471,22 +456,18 @@ Pad.prototype.copy = function copy(destinationID, force, callback) {
async.series([ async.series([
// if it's a group pad, let's make sure the group exists. // if it's a group pad, let's make sure the group exists.
function(callback) function(callback) {
{ if (destinationID.indexOf("$") === -1) {
if (destinationID.indexOf("$") === -1)
{
callback(); callback();
return; return;
} }
destGroupID = destinationID.split("$")[0] destGroupID = destinationID.split("$")[0];
groupManager.doesGroupExist(destGroupID, function (err, exists) groupManager.doesGroupExist(destGroupID, 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 == false) {
{
callback(new customError("groupID does not exist for destinationID", "apierror")); callback(new customError("groupID does not exist for destinationID", "apierror"));
return; return;
} }
@ -495,25 +476,22 @@ Pad.prototype.copy = function copy(destinationID, force, callback) {
callback(); callback();
}); });
}, },
// if the pad exists, we should abort, unless forced. // if the pad exists, we should abort, unless forced.
function(callback) function(callback) {
{ padManager.doesPadExists(destinationID, function (err, exists) {
padManager.doesPadExists(destinationID, function (err, exists)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
/* /*
* this is the negation of a truthy comparison. Has been left in this * this is the negation of a truthy comparison. Has been left in this
* wonky state to keep the old (possibly buggy) behaviour * wonky state to keep the old (possibly buggy) behaviour
*/ */
if (!(exists == true)) if (!(exists == true)) {
{
callback(); callback();
return; return;
} }
if (!force) if (!force) {
{
console.error("erroring out without force"); console.error("erroring out without force");
callback(new customError("destinationID already exists", "apierror")); callback(new customError("destinationID already exists", "apierror"));
console.error("erroring out without force - after"); console.error("erroring out without force - after");
@ -527,26 +505,24 @@ Pad.prototype.copy = function copy(destinationID, force, callback) {
}); });
}); });
}, },
// copy the 'pad' entry // copy the 'pad' entry
function(callback) function(callback) {
{
db.get("pad:" + sourceID, function(err, pad) { db.get("pad:" + sourceID, function(err, pad) {
db.set("pad:" + destinationID, pad); db.set("pad:" + destinationID, pad);
}); });
callback(); callback();
}, },
// copy all relations // copy all relations
function(callback) function(callback) {
{
async.parallel([ async.parallel([
// copy all chat messages // copy all chat messages
function(callback) function(callback) {
{
var chatHead = _this.chatHead; var chatHead = _this.chatHead;
for(var i=0;i<=chatHead;i++) for (var i=0; i <= chatHead; i++) {
{
db.get("pad:" + sourceID + ":chat:" + i, function (err, chat) { db.get("pad:" + sourceID + ":chat:" + i, function (err, chat) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
db.set("pad:" + destinationID + ":chat:" + i, chat); db.set("pad:" + destinationID + ":chat:" + i, chat);
@ -555,12 +531,11 @@ Pad.prototype.copy = function copy(destinationID, force, callback) {
callback(); callback();
}, },
// copy all revisions // copy all revisions
function(callback) function(callback) {
{
var revHead = _this.head; var revHead = _this.head;
for(var i=0;i<=revHead;i++) for (var i=0; i <= revHead; i++) {
{
db.get("pad:" + sourceID + ":revs:" + i, function (err, rev) { db.get("pad:" + sourceID + ":revs:" + i, function (err, rev) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
db.set("pad:" + destinationID + ":revs:" + i, rev); db.set("pad:" + destinationID + ":revs:" + i, rev);
@ -569,12 +544,11 @@ Pad.prototype.copy = function copy(destinationID, force, callback) {
callback(); callback();
}, },
// add the new pad to all authors who contributed to the old one // add the new pad to all authors who contributed to the old one
function(callback) function(callback) {
{
var authorIDs = _this.getAllAuthors(); var authorIDs = _this.getAllAuthors();
authorIDs.forEach(function (authorID) authorIDs.forEach(function (authorID) {
{
authorManager.addPad(authorID, destinationID); authorManager.addPad(authorID, destinationID);
}); });
@ -583,23 +557,27 @@ Pad.prototype.copy = function copy(destinationID, force, callback) {
// parallel // parallel
], callback); ], callback);
}, },
function(callback) { function(callback) {
if (destGroupID) {
// Group pad? Add it to the group's list // Group pad? Add it to the group's list
if(destGroupID) db.setSub("group:" + destGroupID, ["pads", destinationID], 1); db.setSub("group:" + destGroupID, ["pads", destinationID], 1);
}
// Initialize the new pad (will update the listAllPads cache) // Initialize the new pad (will update the listAllPads cache)
setTimeout(function() { setTimeout(function() {
padManager.getPad(destinationID, null, callback) // this runs too early. padManager.getPad(destinationID, null, callback) // this runs too early.
}, 10); }, 10);
}, },
// let the plugins know the pad was copied // let the plugins know the pad was copied
function(callback) { function(callback) {
hooks.callAll('padCopy', { 'originalPad': _this, 'destinationID': destinationID }); hooks.callAll('padCopy', { 'originalPad': _this, 'destinationID': destinationID });
callback(); callback();
} }
// series // series
], function(err) ],
{ function(err) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, { padID: destinationID }); callback(null, { padID: destinationID });
}); });
@ -614,14 +592,11 @@ Pad.prototype.remove = function remove(callback) {
async.series([ async.series([
// delete all relations // delete all relations
function(callback) function(callback) {
{
async.parallel([ async.parallel([
// is it a group pad? -> delete the entry of this pad in the group // is it a group pad? -> delete the entry of this pad in the group
function(callback) function(callback) {
{ if (padID.indexOf("$") === -1) {
if(padID.indexOf("$") === -1)
{
// it isn't a group pad, nothing to do here // it isn't a group pad, nothing to do here
callback(); callback();
return; return;
@ -630,8 +605,7 @@ Pad.prototype.remove = function remove(callback) {
// it is a group pad // it is a group pad
var groupID = padID.substring(0, padID.indexOf("$")); var groupID = padID.substring(0, padID.indexOf("$"));
db.get("group:" + groupID, function (err, group) db.get("group:" + groupID, function (err, group) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// remove the pad entry // remove the pad entry
@ -643,11 +617,10 @@ Pad.prototype.remove = function remove(callback) {
callback(); callback();
}); });
}, },
// remove the readonly entries // remove the readonly entries
function(callback) function(callback) {
{ readOnlyManager.getReadOnlyId(padID, function(err, readonlyID) {
readOnlyManager.getReadOnlyId(padID, function(err, readonlyID)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
db.remove("pad2readonly:" + padID); db.remove("pad2readonly:" + padID);
@ -656,37 +629,34 @@ Pad.prototype.remove = function remove(callback) {
callback(); callback();
}); });
}, },
// delete all chat messages // delete all chat messages
function(callback) function(callback) {
{
var chatHead = _this.chatHead; var chatHead = _this.chatHead;
for(var i=0;i<=chatHead;i++) for (var i = 0; i <= chatHead; i++) {
{
db.remove("pad:" + padID + ":chat:" + i); db.remove("pad:" + padID + ":chat:" + i);
} }
callback(); callback();
}, },
// delete all revisions // delete all revisions
function(callback) function(callback) {
{
var revHead = _this.head; var revHead = _this.head;
for(var i=0;i<=revHead;i++) for (var i = 0; i <= revHead; i++) {
{
db.remove("pad:" + padID + ":revs:" + i); db.remove("pad:" + padID + ":revs:" + i);
} }
callback(); callback();
}, },
// remove pad from all authors who contributed // remove pad from all authors who contributed
function(callback) function(callback) {
{
var authorIDs = _this.getAllAuthors(); var authorIDs = _this.getAllAuthors();
authorIDs.forEach(function (authorID) authorIDs.forEach(function (authorID) {
{
authorManager.removePad(authorID, padID); authorManager.removePad(authorID, padID);
}); });
@ -694,19 +664,20 @@ Pad.prototype.remove = function remove(callback) {
} }
], callback); ], callback);
}, },
// delete the pad entry and delete pad from padManager // delete the pad entry and delete pad from padManager
function(callback) function(callback) {
{
padManager.removePad(padID); padManager.removePad(padID);
hooks.callAll("padRemove", { 'padID': padID }); hooks.callAll("padRemove", { 'padID': padID });
callback(); callback();
} }
], function(err) ],
{ function(err) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(); 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;
@ -753,19 +724,17 @@ Pad.prototype.getSavedRevisions = function getSavedRevisions() {
/* Crypto helper methods */ /* Crypto helper methods */
function hash(password, salt) function hash(password, salt) {
{
var shasum = crypto.createHash('sha512'); var shasum = crypto.createHash('sha512');
shasum.update(password + salt); shasum.update(password + salt);
return shasum.digest("hex") + "$" + salt; return shasum.digest("hex") + "$" + salt;
} }
function generateSalt() function generateSalt() {
{
return randomString(86); return randomString(86);
} }
function compare(hashStr, password) function compare(hashStr, password) {
{
return hash(password, hashStr.split("$")[1]) === hashStr; return hash(password, hashStr.split("$")[1]) === hashStr;
} }

View file

@ -36,8 +36,7 @@ var db = require("./DB").db;
*/ */
var globalPads = { var globalPads = {
get: function(name) { return this[':'+name]; }, get: function(name) { return this[':'+name]; },
set: function (name, value) set: function(name, value) {
{
this[':'+name] = value; this[':'+name] = value;
}, },
remove: function(name) { remove: function(name) {
@ -54,24 +53,28 @@ var padList = {
list: [], list: [],
sorted : false, sorted : false,
initiated: false, initiated: false,
init: function(cb) init: function(cb) {
{ db.findKeys("pad:*", "*:*:*", function(err, dbData) {
db.findKeys("pad:*", "*:*:*", function(err, dbData)
{
if (ERR(err, cb)) return; if (ERR(err, cb)) return;
if (dbData != null) { if (dbData != null) {
padList.initiated = true padList.initiated = true
dbData.forEach(function(val) { dbData.forEach(function(val) {
padList.addPad(val.replace(/pad:/,""),false); padList.addPad(val.replace(/pad:/,""),false);
}); });
cb && cb()
cb && cb();
} }
}); });
return this; return this;
}, },
load: function(cb) { load: function(cb) {
if(this.initiated) cb && cb() if (this.initiated) {
else this.init(cb) cb && cb();
} else {
this.init(cb);
}
}, },
/** /**
* Returns all pads in alphabetical order as array. * Returns all pads in alphabetical order as array.
@ -82,28 +85,31 @@ var padList = {
padList.list = padList.list.sort(); padList.list = padList.list.sort();
padList.sorted = true; padList.sorted = true;
} }
cb && cb(padList.list); cb && cb(padList.list);
}) })
}, },
addPad: function(name) addPad: function(name) {
{
if (!this.initiated) return; if (!this.initiated) return;
if (this.list.indexOf(name) == -1) { if (this.list.indexOf(name) == -1) {
this.list.push(name); this.list.push(name);
this.sorted = false; this.sorted = false;
} }
}, },
removePad: function(name) removePad: function(name) {
{
if (!this.initiated) return; if (!this.initiated) return;
var index = this.list.indexOf(name); var index = this.list.indexOf(name);
if (index > -1) { if (index > -1) {
this.list.splice(index, 1); this.list.splice(index, 1);
this.sorted = false; this.sorted = false;
} }
} }
}; };
//initialises the allknowing data structure
// initialises the all-knowing data structure
/** /**
* 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
@ -122,43 +128,41 @@ var padIdTransforms = [
exports.getPad = function(id, text, callback) exports.getPad = function(id, text, callback)
{ {
// check if this is a valid padId // check if this is a valid padId
if(!exports.isValidPadId(id)) if (!exports.isValidPadId(id)) {
{
callback(new customError(id + " is not a valid padId", "apierror")); callback(new customError(id + " is not a valid padId", "apierror"));
return; return;
} }
// make text an optional parameter // make text an optional parameter
if(typeof text == "function") if (typeof text == "function") {
{
callback = text; callback = text;
text = null; text = null;
} }
// check if this is a valid text // check if this is a valid text
if(text != null) if (text != null) {
{
// check if text is a string // check if text is a string
if(typeof text != "string") if (typeof text != "string") {
{
callback(new customError("text is not a string", "apierror")); callback(new customError("text is not a string", "apierror"));
return; return;
} }
// check if text is less than 100k chars // check if text is less than 100k chars
if(text.length > 100000) if (text.length > 100000) {
{
callback(new customError("text must be less than 100k chars", "apierror")); callback(new customError("text must be less than 100k chars", "apierror"));
return; return;
} }
} }
var pad = globalPads.get(id); var pad = globalPads.get(id);
//return pad if its already loaded // return pad if it's already loaded
if(pad != null) if (pad != null) {
{
callback(null, pad); callback(null, pad);
return; return;
} }
@ -166,9 +170,9 @@ exports.getPad = function(id, text, callback)
pad = new Pad(id); pad = new Pad(id);
// initalize the pad // initalize the pad
pad.init(text, function(err) pad.init(text, function(err) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
globalPads.set(id, pad); globalPads.set(id, pad);
padList.addPad(id); padList.addPad(id);
callback(null, pad); callback(null, pad);
@ -185,14 +189,12 @@ exports.listAllPads = function(cb)
// checks if a pad exists // checks if a pad exists
exports.doesPadExists = function(padId, callback) exports.doesPadExists = function(padId, callback)
{ {
db.get("pad:"+padId, function(err, value) db.get("pad:" + padId, function(err, value) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
if (value != null && value.atext) { if (value != null && value.atext) {
callback(null, true); callback(null, true);
} } else {
else
{
callback(null, false); callback(null, false);
} }
}); });
@ -201,29 +203,30 @@ exports.doesPadExists = function(padId, callback)
// returns a sanitized padId, respecting legacy pad id formats // returns a sanitized padId, respecting legacy pad id formats
exports.sanitizePadId = function(padId, callback) { exports.sanitizePadId = function(padId, callback) {
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) {
{
callback(padId); callback(padId);
return; return;
} }
// check if padId exists // check if padId exists
exports.doesPadExists(padId, function(junk, exists) exports.doesPadExists(padId, function(junk, exists) {
{ if (exists) {
if(exists)
{
callback(padId); callback(padId);
return; return;
} }
// get the next transformation *that's different* // get the next transformation *that's different*
var transformedPadId = padId; var transformedPadId = padId;
while(transformedPadId == padId && transform_index < padIdTransforms.length)
{ while(transformedPadId == padId && transform_index < padIdTransforms.length) {
transformedPadId = padId.replace(padIdTransforms[transform_index][0], padIdTransforms[transform_index][1]); transformedPadId = padId.replace(padIdTransforms[transform_index][0], padIdTransforms[transform_index][1]);
transform_index += 1; transform_index += 1;
} }
// check the next transform // check the next transform
exports.sanitizePadId(transformedPadId, callback, transform_index); exports.sanitizePadId(transformedPadId, callback, transform_index);
}); });

View file

@ -34,38 +34,35 @@ exports.getReadOnlyId = function (padId, callback)
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); db.get("pad2readonly:" + padId, callback);
}, },
function(dbReadOnlyId, callback)
{ function(dbReadOnlyId, callback) {
if (dbReadOnlyId == null) {
// there is no readOnly Entry in the database, let's create one // there is no readOnly Entry in the database, let's create one
if(dbReadOnlyId == null)
{
readOnlyId = "r." + randomString(16); readOnlyId = "r." + randomString(16);
db.set("pad2readonly:" + padId, readOnlyId); db.set("pad2readonly:" + padId, readOnlyId);
db.set("readonly2pad:" + readOnlyId, padId); db.set("readonly2pad:" + readOnlyId, padId);
} } else {
// 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
{
readOnlyId = dbReadOnlyId; readOnlyId = dbReadOnlyId;
} }
callback(); callback();
} }
], function(err) ],
{ function(err) {
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 the padId for a read only id
* @param {String} readOnlyId read only id * @param {String} readOnlyId read only id
*/ */
exports.getPadId = function(readOnlyId, callback) exports.getPadId = function(readOnlyId, callback)
@ -74,20 +71,21 @@ exports.getPadId = function(readOnlyId, callback)
} }
/** /**
* returns a the padId and readonlyPadId in an object for any id * returns the padId and readonlyPadId in an object for any id
* @param {String} padIdOrReadonlyPadId read only id or real pad id * @param {String} padIdOrReadonlyPadId read only id or real pad id
*/ */
exports.getIds = function(id, callback) { exports.getIds = function(id, callback) {
if (id.indexOf("r.") == 0) if (id.indexOf("r.") == 0) {
exports.getPadId(id, function (err, value) { exports.getPadId(id, function (err, value) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, { callback(null, {
readOnlyPadId: id, readOnlyPadId: id,
padId: value, // Might be null, if this is an unknown read-only id padId: value, // Might be null, if this is an unknown read-only id
readonly: true readonly: true
}); });
}); });
else } else {
exports.getReadOnlyId(id, function (err, value) { exports.getReadOnlyId(id, function (err, value) {
callback(null, { callback(null, {
readOnlyPadId: value, readOnlyPadId: value,
@ -96,3 +94,4 @@ exports.getIds = function(id, callback) {
}); });
}); });
} }
}

View file

@ -18,7 +18,6 @@
* limitations under the License. * limitations under the License.
*/ */
var ERR = require("async-stacktrace"); var ERR = require("async-stacktrace");
var async = require("async"); var async = require("async");
var authorManager = require("./AuthorManager"); var authorManager = require("./AuthorManager");
@ -48,45 +47,43 @@ exports.checkAccess = function (padID, sessionCookie, token, password, callback)
// allow plugins to deny access // allow plugins to deny access
var deniedByHook = hooks.callAll("onAccessCheck", {'padID': padID, 'password': password, 'token': token, 'sessionCookie': sessionCookie}).indexOf(false) > -1; var deniedByHook = hooks.callAll("onAccessCheck", {'padID': padID, 'password': password, 'token': token, 'sessionCookie': sessionCookie}).indexOf(false) > -1;
if(deniedByHook) if (deniedByHook) {
{
callback(null, {accessStatus: "deny"}); callback(null, {accessStatus: "deny"});
return; return;
} }
if (settings.requireSession) {
// a valid session is required (api-only mode) // a valid session is required (api-only mode)
if(settings.requireSession) if (!sessionCookie) {
{
// without sessionCookie, access is denied // without sessionCookie, access is denied
if(!sessionCookie)
{
callback(null, {accessStatus: "deny"}); callback(null, {accessStatus: "deny"});
return; return;
} }
} } else {
// a session is not required, so we'll check if it's a public pad // a session is not required, so we'll check if it's a public pad
else if (padID.indexOf("$") == -1) {
{
// it's not a group pad, means we can grant access // it's not a group pad, means we can grant access
if(padID.indexOf("$") == -1)
{
// get author for this token // get author for this token
authorManager.getAuthor4Token(token, function(err, author) 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 };
if (settings.editOnly) {
// user can't create pads // user can't create pads
if(settings.editOnly)
{
// check if pad exists // check if pad exists
padManager.doesPadExists(padID, function(err, exists) padManager.doesPadExists(padID, function(err, exists) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
if (!exists) {
// pad doesn't exist - user can't have access // pad doesn't exist - user can't have access
if(!exists) statusObject.accessStatus = "deny"; statusObject.accessStatus = "deny";
}
// grant or deny access, with author of token // grant or deny access, with author of token
callback(null, statusObject); callback(null, statusObject);
}); });
@ -115,38 +112,34 @@ exports.checkAccess = function (padID, sessionCookie, token, password, callback)
async.series([ async.series([
// get basic informations from the database // get basic informations from the database
function(callback) function(callback) {
{
async.parallel([ async.parallel([
//does pad exists // does pad exist
function(callback) function(callback) {
{ padManager.doesPadExists(padID, function(err, exists) {
padManager.doesPadExists(padID, function(err, exists)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
padExists = exists; padExists = exists;
callback(); callback();
}); });
}, },
// get information about all sessions contained in this cookie // get information about all sessions contained in this cookie
function(callback) function(callback) {
{ if (!sessionCookie) {
if (!sessionCookie)
{
callback(); callback();
return; return;
} }
var sessionIDs = sessionCookie.split(','); var sessionIDs = sessionCookie.split(',');
async.forEach(sessionIDs, function(sessionID, callback)
{ async.forEach(sessionIDs, function(sessionID, callback) {
sessionManager.getSessionInfo(sessionID, function(err, sessionInfo) sessionManager.getSessionInfo(sessionID, function(err, sessionInfo) {
{
// skip session if it doesn't exist // skip session if it doesn't exist
if(err && err.message == "sessionID does not exist") if (err && err.message == "sessionID does not exist") {
{
authLogger.debug("Auth failed: unknown session"); authLogger.debug("Auth failed: unknown session");
callback(); callback();
return; return;
} }
@ -155,18 +148,18 @@ exports.checkAccess = function (padID, sessionCookie, token, password, callback)
var now = Math.floor(Date.now()/1000); var now = Math.floor(Date.now()/1000);
// is it for this group? // is it for this group?
if(sessionInfo.groupID != groupID) if (sessionInfo.groupID != groupID) {
{
authLogger.debug("Auth failed: wrong group"); authLogger.debug("Auth failed: wrong group");
callback(); callback();
return; return;
} }
// is validUntil still ok? // is validUntil still ok?
if(sessionInfo.validUntil <= now) if (sessionInfo.validUntil <= now) {
{
authLogger.debug("Auth failed: validUntil"); authLogger.debug("Auth failed: validUntil");
callback(); callback();
return; return;
} }
@ -178,31 +171,30 @@ exports.checkAccess = function (padID, sessionCookie, token, password, callback)
}); });
}, callback); }, callback);
}, },
// get author for token // get author for token
function(callback) function(callback) {
{
// get author for this token // get author for this token
authorManager.getAuthor4Token(token, function(err, author) authorManager.getAuthor4Token(token, function(err, author) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
tokenAuthor = author; tokenAuthor = author;
callback(); callback();
}); });
} }
], callback); ], callback);
}, },
// get more informations of this pad, if avaiable // get more informations of this pad, if avaiable
function(callback) function(callback) {
{ // skip this if the pad doesn't exist
//skip this if the pad doesn't exists if (padExists == false) {
if(padExists == false)
{
callback(); callback();
return; return;
} }
padManager.getPad(padID, function(err, pad) padManager.getPad(padID, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
// is it a public pad? // is it a public pad?
@ -212,108 +204,89 @@ exports.checkAccess = function (padID, sessionCookie, token, password, callback)
isPasswordProtected = pad.isPasswordProtected(); isPasswordProtected = pad.isPasswordProtected();
// is password correct? // is password correct?
if(isPasswordProtected && password && pad.isCorrectPassword(password)) if (isPasswordProtected && password && pad.isCorrectPassword(password)) {
{
passwordStatus = "correct"; passwordStatus = "correct";
} }
callback(); callback();
}); });
}, },
function(callback)
{ function(callback) {
if (validSession && padExists) {
// - a valid session for this group is avaible AND pad exists // - a valid session for this group is avaible AND pad exists
if(validSession && padExists) if (!isPasswordProtected) {
{
// - the pad is not password protected // - the pad is not password protected
if(!isPasswordProtected)
{
// --> grant access // --> grant access
statusObject = { accessStatus: "grant", authorID: sessionAuthor }; statusObject = { accessStatus: "grant", authorID: sessionAuthor };
} } else if (settings.sessionNoPassword) {
// - the setting to bypass password validation is set // - the setting to bypass password validation is set
else if(settings.sessionNoPassword)
{
// --> grant access // --> grant access
statusObject = { accessStatus: "grant", authorID: sessionAuthor }; statusObject = { accessStatus: "grant", authorID: sessionAuthor };
} } else if (isPasswordProtected && passwordStatus == "correct") {
// - the pad is password protected and password is correct // - the pad is password protected and password is correct
else if(isPasswordProtected && passwordStatus == "correct")
{
// --> grant access // --> grant access
statusObject = { accessStatus: "grant", authorID: sessionAuthor }; statusObject = { accessStatus: "grant", authorID: sessionAuthor };
} } else if (isPasswordProtected && passwordStatus == "wrong") {
// - the pad is password protected but wrong password given // - the pad is password protected but wrong password given
else if(isPasswordProtected && passwordStatus == "wrong")
{
// --> deny access, ask for new password and tell them that the password is wrong // --> deny access, ask for new password and tell them that the password is wrong
statusObject = { accessStatus: "wrongPassword" }; statusObject = { accessStatus: "wrongPassword" };
} } else if (isPasswordProtected && passwordStatus == "notGiven") {
// - the pad is password protected but no password given // - the pad is password protected but no password given
else if(isPasswordProtected && passwordStatus == "notGiven")
{
// --> ask for password // --> ask for password
statusObject = { accessStatus: "needPassword" }; statusObject = { accessStatus: "needPassword" };
} } else {
else
{
throw new Error("Ops, something wrong happend"); throw new Error("Ops, something wrong happend");
} }
} } else if (validSession && !padExists) {
//- a valid session for this group avaible but pad doesn't exists // - a valid session for this group avaible but pad doesn't exist
else if(validSession && !padExists)
{
// --> grant access // --> grant access
statusObject = {accessStatus: "grant", authorID: sessionAuthor}; statusObject = {accessStatus: "grant", authorID: sessionAuthor};
if (settings.editOnly) {
// --> 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)
{
authLogger.debug("Auth failed: valid session & pad does not exist"); authLogger.debug("Auth failed: valid session & pad does not exist");
statusObject.accessStatus = "deny"; statusObject.accessStatus = "deny";
} }
} } else if (!validSession && padExists) {
// there is no valid session avaiable AND pad exists // there is no valid session avaiable AND pad exists
else if(!validSession && padExists)
{ // -- it's public and not password protected
//-- its public and not password protected if (isPublic && !isPasswordProtected) {
if(isPublic && !isPasswordProtected)
{
// --> grant access, with author of token // --> grant access, with author of token
statusObject = {accessStatus: "grant", authorID: tokenAuthor}; statusObject = {accessStatus: "grant", authorID: tokenAuthor};
} } else if (isPublic && isPasswordProtected && passwordStatus == "correct") {
//- its public and password protected and password is correct // - it's public and password protected and password is correct
else if(isPublic && isPasswordProtected && passwordStatus == "correct")
{
// --> grant access, with author of token // --> grant access, with author of token
statusObject = {accessStatus: "grant", authorID: tokenAuthor}; statusObject = {accessStatus: "grant", authorID: tokenAuthor};
} } else if (isPublic && isPasswordProtected && passwordStatus == "wrong") {
//- its public and the pad is password protected but wrong password given // - it's public and the pad is password protected but wrong password given
else if(isPublic && isPasswordProtected && passwordStatus == "wrong")
{
// --> deny access, ask for new password and tell them that the password is wrong // --> deny access, ask for new password and tell them that the password is wrong
statusObject = {accessStatus: "wrongPassword"}; statusObject = {accessStatus: "wrongPassword"};
} } else if (isPublic && isPasswordProtected && passwordStatus == "notGiven") {
//- its public and the pad is password protected but no password given // - it's public and the pad is password protected but no password given
else if(isPublic && isPasswordProtected && passwordStatus == "notGiven")
{
// --> ask for password // --> ask for password
statusObject = {accessStatus: "needPassword"}; statusObject = {accessStatus: "needPassword"};
} } else if (!isPublic) {
//- its not public // - it's not public
else if(!isPublic)
{
authLogger.debug("Auth failed: invalid session & pad is not public"); authLogger.debug("Auth failed: invalid session & pad is not public");
// --> deny access // --> deny access
statusObject = {accessStatus: "deny"}; statusObject = {accessStatus: "deny"};
} } else {
else
{
throw new Error("Ops, something wrong happend"); throw new Error("Ops, something wrong happend");
} }
} } else {
// there is no valid session avaiable AND pad doesn't exists // there is no valid session avaiable AND pad doesn't exist
else
{
authLogger.debug("Auth failed: invalid session & pad does not exist"); authLogger.debug("Auth failed: invalid session & pad does not exist");
// --> deny access // --> deny access
statusObject = {accessStatus: "deny"}; statusObject = {accessStatus: "deny"};
@ -321,9 +294,10 @@ exports.checkAccess = function (padID, sessionCookie, token, password, callback)
callback(); callback();
} }
], function(err) ],
{ function(err) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, statusObject); callback(null, statusObject);
}); });
}; };

View file

@ -18,7 +18,6 @@
* limitations under the License. * limitations under the License.
*/ */
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");
@ -45,7 +44,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
var sessionID; var sessionID;
async.series([ async.series([
//check if group exists // check if the group exists
function(callback) function(callback)
{ {
groupMangager.doesGroupExist(groupID, function(err, exists) groupMangager.doesGroupExist(groupID, function(err, exists)
@ -53,36 +52,32 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
// group does not exist // group does not exist
if(exists == false) if (exists == false) {
{
callback(new customError("groupID does not exist", "apierror")); callback(new customError("groupID does not exist", "apierror"));
} } else {
// everything is fine, continue // everything is fine, continue
else
{
callback(); callback();
} }
}); });
}, },
//check if author exists
// check if the author exists
function(callback) function(callback)
{ {
authorMangager.doesAuthorExists(authorID, function(err, exists) authorMangager.doesAuthorExists(authorID, function(err, exists)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
if (exists == false) {
// author does not exist // author does not exist
if(exists == false)
{
callback(new customError("authorID does not exist", "apierror")); callback(new customError("authorID does not exist", "apierror"));
} } else {
// everything is fine, continue // everything is fine, continue
else
{
callback(); callback();
} }
}); });
}, },
// check validUntil and create the session db entry // check validUntil and create the session db entry
function(callback) function(callback)
{ {
@ -90,8 +85,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
if (typeof validUntil != "number") if (typeof validUntil != "number")
{ {
// try to parse the number // try to parse the number
if(isNaN(parseInt(validUntil))) if (isNaN(parseInt(validUntil))) {
{
callback(new customError("validUntil is not a number", "apierror")); callback(new customError("validUntil is not a number", "apierror"));
return; return;
} }
@ -99,23 +93,20 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
validUntil = parseInt(validUntil); validUntil = parseInt(validUntil);
} }
//ensure this is not a negativ number // ensure this is not a negative number
if(validUntil < 0) if (validUntil < 0) {
{
callback(new customError("validUntil is a negativ number", "apierror")); callback(new customError("validUntil is a negativ number", "apierror"));
return; return;
} }
// ensure this is not a float value // ensure this is not a float value
if(!is_int(validUntil)) if (!is_int(validUntil)) {
{
callback(new customError("validUntil is a float value", "apierror")); callback(new customError("validUntil is a float value", "apierror"));
return; return;
} }
// check if validUntil is in the future // check if validUntil is in the future
if(Math.floor(Date.now()/1000) > validUntil) if (Math.floor(Date.now()/1000) > validUntil) {
{
callback(new customError("validUntil is in the past", "apierror")); callback(new customError("validUntil is in the past", "apierror"));
return; return;
} }
@ -128,6 +119,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
callback(); callback();
}, },
// set the group2sessions entry // set the group2sessions entry
function(callback) function(callback)
{ {
@ -136,9 +128,8 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
if (group2sessions == null || group2sessions.sessionIDs == null) {
// the entry doesn't exist so far, let's create it // the entry doesn't exist so far, let's create it
if(group2sessions == null || group2sessions.sessionIDs == null)
{
group2sessions = {sessionIDs : {}}; group2sessions = {sessionIDs : {}};
} }
@ -151,6 +142,7 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
callback(); callback();
}); });
}, },
// set the author2sessions entry // set the author2sessions entry
function(callback) function(callback)
{ {
@ -159,9 +151,8 @@ exports.createSession = function(groupID, authorID, validUntil, callback)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
if (author2sessions == null || author2sessions.sessionIDs == null) {
// the entry doesn't exist so far, let's create it // the entry doesn't exist so far, let's create it
if(author2sessions == null || author2sessions.sessionIDs == null)
{
author2sessions = {sessionIDs : {}}; author2sessions = {sessionIDs : {}};
} }
@ -190,14 +181,11 @@ exports.getSessionInfo = function(sessionID, callback)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
//session does not exists if (session == null) {
if(session == null) // session does not exist
{
callback(new customError("sessionID does not exist", "apierror")) callback(new customError("sessionID does not exist", "apierror"))
} } else {
// everything is fine, return the sessioninfos // everything is fine, return the sessioninfos
else
{
callback(null, session); callback(null, session);
} }
}); });
@ -219,14 +207,11 @@ exports.deleteSession = function(sessionID, callback)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
//session does not exists if (session == null) {
if(session == null) // session does not exist
{
callback(new customError("sessionID does not exist", "apierror")) callback(new customError("sessionID does not exist", "apierror"))
} } else {
//everything is fine, return the sessioninfos // everything is fine, use the sessioninfos
else
{
authorID = session.authorID; authorID = session.authorID;
groupID = session.groupID; groupID = session.groupID;
@ -234,6 +219,7 @@ exports.deleteSession = function(sessionID, callback)
} }
}); });
}, },
// get the group2sessions entry // get the group2sessions entry
function(callback) function(callback)
{ {
@ -244,6 +230,7 @@ exports.deleteSession = function(sessionID, callback)
callback(); callback();
}); });
}, },
// get the author2sessions entry // get the author2sessions entry
function(callback) function(callback)
{ {
@ -254,6 +241,7 @@ exports.deleteSession = function(sessionID, callback)
callback(); callback();
}); });
}, },
// remove the values from the database // remove the values from the database
function(callback) function(callback)
{ {
@ -287,14 +275,11 @@ exports.listSessionsOfGroup = function(groupID, callback)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
if (exists == false) {
// group does not exist // group does not exist
if(exists == false)
{
callback(new customError("groupID does not exist", "apierror")); callback(new customError("groupID does not exist", "apierror"));
} } else {
// everything is fine, continue // everything is fine, continue
else
{
listSessionsWithDBKey("group2sessions:" + groupID, callback); listSessionsWithDBKey("group2sessions:" + groupID, callback);
} }
}); });
@ -306,20 +291,17 @@ exports.listSessionsOfAuthor = function(authorID, callback)
{ {
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
if (exists == false) {
// group does not exist // group does not exist
if(exists == false)
{
callback(new customError("authorID does not exist", "apierror")); callback(new customError("authorID does not exist", "apierror"));
} } else {
// everything is fine, continue // everything is fine, continue
else
{
listSessionsWithDBKey("author2sessions:" + authorID, callback); listSessionsWithDBKey("author2sessions:" + authorID, callback);
} }
}); });
} }
//this function is basicly the code listSessionsOfAuthor and listSessionsOfGroup has in common // this function is basically the code listSessionsOfAuthor and listSessionsOfGroup has in common
function listSessionsWithDBKey (dbkey, callback) function listSessionsWithDBKey (dbkey, callback)
{ {
var sessions; var sessions;
@ -335,6 +317,7 @@ function listSessionsWithDBKey (dbkey, callback)
callback(); callback();
}); });
}, },
function(callback) function(callback)
{ {
// collect all sessionIDs in an arrary // collect all sessionIDs in an arrary
@ -344,17 +327,14 @@ function listSessionsWithDBKey (dbkey, callback)
sessionIDs.push(i); sessionIDs.push(i);
} }
//foreach trough the sessions and get the sessioninfos // iterate through the sessions and get the sessioninfos
async.forEach(sessionIDs, function(sessionID, callback) async.forEach(sessionIDs, function(sessionID, callback)
{ {
exports.getSessionInfo(sessionID, function(err, sessionInfo) exports.getSessionInfo(sessionID, function(err, sessionInfo)
{ {
if (err == "apierror: sessionID does not exist") if (err == "apierror: sessionID does not exist") {
{
console.warn(`Found bad session ${sessionID} in ${dbkey}`); console.warn(`Found bad session ${sessionID} in ${dbkey}`);
} } else if(ERR(err, callback)) {
else if(ERR(err, callback))
{
return; return;
} }
@ -373,5 +353,5 @@ 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)) && !isNaN(value) return (parseFloat(value) == parseInt(value)) && !isNaN(value);
} }

View file

@ -15,9 +15,10 @@ SessionStore.prototype.__proto__ = Store.prototype;
SessionStore.prototype.get = function(sid, fn) { SessionStore.prototype.get = function(sid, fn) {
messageLogger.debug('GET ' + sid); messageLogger.debug('GET ' + sid);
var self = this; var self = this;
db.get("sessionstorage:" + sid, function (err, sess)
{ db.get("sessionstorage:" + sid, function(err, sess) {
if (sess) { if (sess) {
sess.cookie.expires = 'string' == typeof sess.cookie.expires ? new Date(sess.cookie.expires) : sess.cookie.expires; sess.cookie.expires = 'string' == typeof sess.cookie.expires ? new Date(sess.cookie.expires) : sess.cookie.expires;
if (!sess.cookie.expires || new Date() < sess.cookie.expires) { if (!sess.cookie.expires || new Date() < sess.cookie.expires) {
@ -33,6 +34,7 @@ SessionStore.prototype.get = function(sid, fn){
SessionStore.prototype.set = function(sid, sess, fn) { SessionStore.prototype.set = function(sid, sess, fn) {
messageLogger.debug('SET ' + sid); messageLogger.debug('SET ' + sid);
db.set("sessionstorage:" + sid, sess); db.set("sessionstorage:" + sid, sess);
process.nextTick(function() { process.nextTick(function() {
if (fn) fn(); if (fn) fn();
@ -41,6 +43,7 @@ SessionStore.prototype.set = function(sid, sess, fn){
SessionStore.prototype.destroy = function(sid, fn) { SessionStore.prototype.destroy = function(sid, fn) {
messageLogger.debug('DESTROY ' + sid); messageLogger.debug('DESTROY ' + sid);
db.remove("sessionstorage:" + sid); db.remove("sessionstorage:" + sid);
process.nextTick(function() { process.nextTick(function() {
if (fn) fn(); if (fn) fn();
@ -49,7 +52,9 @@ SessionStore.prototype.destroy = function(sid, fn){
SessionStore.prototype.all = function(fn) { SessionStore.prototype.all = function(fn) {
messageLogger.debug('ALL'); messageLogger.debug('ALL');
var sessions = []; var sessions = [];
db.forEach(function(key, value) { db.forEach(function(key, value) {
if (key.substr(0,15) === "sessionstorage:") { if (key.substr(0,15) === "sessionstorage:") {
sessions.push(value); sessions.push(value);
@ -60,6 +65,7 @@ SessionStore.prototype.all = function(fn){
SessionStore.prototype.clear = function(fn) { SessionStore.prototype.clear = function(fn) {
messageLogger.debug('CLEAR'); messageLogger.debug('CLEAR');
db.forEach(function(key, value) { db.forEach(function(key, value) {
if (key.substr(0,15) === "sessionstorage:") { if (key.substr(0,15) === "sessionstorage:") {
db.db.remove("session:" + key); db.db.remove("session:" + key);
@ -70,7 +76,9 @@ SessionStore.prototype.clear = function(fn){
SessionStore.prototype.length = function(fn) { SessionStore.prototype.length = function(fn) {
messageLogger.debug('LENGTH'); messageLogger.debug('LENGTH');
var i = 0; var i = 0;
db.forEach(function(key, value) { db.forEach(function(key, value) {
if (key.substr(0,15) === "sessionstorage:") { if (key.substr(0,15) === "sessionstorage:") {
i++; i++;

View file

@ -32,13 +32,11 @@ var apiHandlerLogger = log4js.getLogger('APIHandler');
//ensure we have an apikey //ensure we have an apikey
var apikey = null; var apikey = null;
var apikeyFilename = absolutePaths.makeAbsolute(argv.apikey || "./APIKEY.txt"); var apikeyFilename = absolutePaths.makeAbsolute(argv.apikey || "./APIKEY.txt");
try
{ try {
apikey = fs.readFileSync(apikeyFilename,"utf8"); apikey = fs.readFileSync(apikeyFilename,"utf8");
apiHandlerLogger.info(`Api key file read from: "${apikeyFilename}"`); apiHandlerLogger.info(`Api key file read from: "${apikeyFilename}"`);
} } catch(e) {
catch(e)
{
apiHandlerLogger.info(`Api key file "${apikeyFilename}" not found. Creating with random contents.`); apiHandlerLogger.info(`Api key file "${apikeyFilename}" not found. Creating with random contents.`);
apikey = randomString(32); apikey = randomString(32);
fs.writeFileSync(apikeyFilename,apikey,"utf8"); fs.writeFileSync(apikeyFilename,apikey,"utf8");
@ -156,18 +154,16 @@ exports.handle = function(apiVersion, functionName, fields, req, res)
{ {
//check if this is a valid apiversion //check if this is a valid apiversion
var isKnownApiVersion = false; var isKnownApiVersion = false;
for(var knownApiVersion in version)
{ for (var knownApiVersion in version) {
if(knownApiVersion == apiVersion) if (knownApiVersion == apiVersion) {
{
isKnownApiVersion = true; isKnownApiVersion = true;
break; break;
} }
} }
// say goodbye if this is an unknown API version // say goodbye if this is an unknown API version
if(!isKnownApiVersion) if (!isKnownApiVersion) {
{
res.statusCode = 404; res.statusCode = 404;
res.send({code: 3, message: "no such api version", data: null}); res.send({code: 3, message: "no such api version", data: null});
return; return;
@ -175,18 +171,16 @@ exports.handle = function(apiVersion, functionName, fields, req, res)
// check if this is a valid function name // check if this is a valid function name
var isKnownFunctionname = false; var isKnownFunctionname = false;
for(var knownFunctionname in version[apiVersion])
{ for (var knownFunctionname in version[apiVersion]) {
if(knownFunctionname == functionName) if (knownFunctionname == functionName) {
{
isKnownFunctionname = true; isKnownFunctionname = true;
break; break;
} }
} }
//say goodbye if this is a unknown function // say goodbye if this is an unknown function
if(!isKnownFunctionname) if (!isKnownFunctionname) {
{
res.send({code: 3, message: "no such function", data: null}); res.send({code: 3, message: "no such function", data: null});
return; return;
} }
@ -194,32 +188,24 @@ exports.handle = function(apiVersion, functionName, fields, req, res)
// check the api key! // check the api key!
fields["apikey"] = fields["apikey"] || fields["api_key"]; fields["apikey"] = fields["apikey"] || fields["api_key"];
if(fields["apikey"] != apikey.trim()) if (fields["apikey"] != apikey.trim()) {
{
res.statusCode = 401; res.statusCode = 401;
res.send({code: 4, message: "no or wrong API Key", data: null}); res.send({code: 4, message: "no or wrong API Key", data: null});
return; return;
} }
//sanitize any pad id's before continuing // sanitize any padIDs before continuing
if(fields["padID"]) if (fields["padID"]) {
{ padManager.sanitizePadId(fields["padID"], function(padId) {
padManager.sanitizePadId(fields["padID"], function(padId)
{
fields["padID"] = padId; fields["padID"] = padId;
callAPI(apiVersion, functionName, fields, req, res); callAPI(apiVersion, functionName, fields, req, res);
}); });
} } else if (fields["padName"]) {
else if(fields["padName"]) padManager.sanitizePadId(fields["padName"], function(padId) {
{
padManager.sanitizePadId(fields["padName"], function(padId)
{
fields["padName"] = padId; fields["padName"] = padId;
callAPI(apiVersion, functionName, fields, req, res); callAPI(apiVersion, functionName, fields, req, res);
}); });
} } else {
else
{
callAPI(apiVersion, functionName, fields, req, res); callAPI(apiVersion, functionName, fields, req, res);
} }
} }
@ -230,27 +216,25 @@ function callAPI(apiVersion, functionName, fields, req, res)
// put the function parameters in an array // put the function parameters in an array
var functionParams = version[apiVersion][functionName].map(function (field) { var functionParams = version[apiVersion][functionName].map(function (field) {
return fields[field] return fields[field]
}) });
// add a callback function to handle the response // add a callback function to handle the response
functionParams.push(function(err, data) functionParams.push(function(err, data) {
{ if (err == null) {
// no error happend, everything is fine // no error happened, everything is fine
if(err == null)
{ if (!data) {
if(!data)
data = null; data = null;
}
res.send({code: 0, message: "ok", data: data}); res.send({code: 0, message: "ok", data: data});
} } else if (err.name == "apierror") {
// parameters were wrong and the api stopped execution, pass the error // parameters were wrong and the api stopped execution, pass the error
else if(err.name == "apierror")
{
res.send({code: 1, message: err.message, data: null}); res.send({code: 1, message: err.message, data: null});
} } else {
//an unknown error happend // an unknown error happened
else
{
res.send({code: 2, message: "internal error", data: null}); res.send({code: 2, message: "internal error", data: null});
ERR(err); ERR(err);
} }

View file

@ -32,13 +32,15 @@ var TidyHtml = require('../utils/TidyHtml');
var convertor = null; var convertor = null;
//load abiword only if its enabled // load abiword only if it is enabled
if(settings.abiword != null) if (settings.abiword != null) {
convertor = require("../utils/Abiword"); convertor = require("../utils/Abiword");
}
// Use LibreOffice if an executable has been defined in the settings // Use LibreOffice if an executable has been defined in the settings
if(settings.soffice != null) if (settings.soffice != null) {
convertor = require("../utils/LibreOffice"); convertor = require("../utils/LibreOffice");
}
const tempDirectory = os.tmpdir(); const tempDirectory = os.tmpdir();
@ -53,7 +55,9 @@ exports.doExport = function(req, res, padId, type)
hooks.aCallFirst("exportFileName", padId, hooks.aCallFirst("exportFileName", padId,
function(err, hookFileName){ function(err, hookFileName){
// if fileName is set then set it to the padId, note that fileName is returned as an array. // if fileName is set then set it to the padId, note that fileName is returned as an array.
if(hookFileName.length) fileName = hookFileName; if (hookFileName.length) {
fileName = hookFileName;
}
// tell the browser that this is a downloadable file // tell the browser that this is a downloadable file
res.attachment(fileName + "." + type); res.attachment(fileName + "." + type);
@ -67,48 +71,39 @@ exports.doExport = function(req, res, padId, type)
// return; // return;
} }
}); });
} } else if (type == "txt") {
else if(type == "txt") exporttxt.getPadTXTDocument(padId, req.params.rev, function(err, txt) {
{
exporttxt.getPadTXTDocument(padId, req.params.rev, function(err, txt)
{
if (!err) { if (!err) {
res.send(txt); res.send(txt);
} }
}); });
} } else {
else
{
var html; var html;
var randNum; var randNum;
var srcFile, destFile; var srcFile, destFile;
async.series([ async.series([
// render the html document // render the html document
function(callback) function(callback) {
{ exporthtml.getPadHTMLDocument(padId, req.params.rev, function(err, _html) {
exporthtml.getPadHTMLDocument(padId, req.params.rev, function(err, _html)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
html = _html; html = _html;
callback(); callback();
}); });
}, },
// decide what to do with the html export // decide what to do with the html export
function(callback) function(callback) {
{
// if this is a html export, we can send this from here directly // if this is a html export, we can send this from here directly
if(type == "html") if (type == "html") {
{
// do any final changes the plugin might want to make // do any final changes the plugin might want to make
hooks.aCallFirst("exportHTMLSend", html, function(err, newHTML) { hooks.aCallFirst("exportHTMLSend", html, function(err, newHTML) {
if (newHTML.length) html = newHTML; if (newHTML.length) html = newHTML;
res.send(html); res.send(html);
callback("stop"); callback("stop");
}); });
} } else {
else //write the html export to a file // write the html export to a file
{
randNum = Math.floor(Math.random()*0xFFFFFFFF); randNum = Math.floor(Math.random()*0xFFFFFFFF);
srcFile = tempDirectory + "/etherpad_export_" + randNum + ".html"; srcFile = tempDirectory + "/etherpad_export_" + randNum + ".html";
fs.writeFile(srcFile, html, callback); fs.writeFile(srcFile, html, callback);
@ -116,8 +111,7 @@ exports.doExport = function(req, res, padId, type)
}, },
// Tidy up the exported HTML // Tidy up the exported HTML
function(callback) function(callback) {
{
// ensure html can be collected by the garbage collector // ensure html can be collected by the garbage collector
html = null; html = null;
@ -125,8 +119,7 @@ exports.doExport = function(req, res, padId, type)
}, },
// send the convert job to the convertor (abiword, libreoffice, ..) // send the convert job to the convertor (abiword, libreoffice, ..)
function(callback) function(callback) {
{
destFile = tempDirectory + "/etherpad_export_" + randNum + "." + type; destFile = tempDirectory + "/etherpad_export_" + randNum + "." + type;
// Allow plugins to overwrite the convert in export process // Allow plugins to overwrite the convert in export process
@ -141,38 +134,32 @@ exports.doExport = function(req, res, padId, type)
}); });
}, },
// send the file // send the file
function(callback) function(callback) {
{
res.sendFile(destFile, null, callback); res.sendFile(destFile, null, callback);
}, },
// clean up temporary files // clean up temporary files
function(callback) function(callback) {
{
async.parallel([ async.parallel([
function(callback) function(callback) {
{
fs.unlink(srcFile, callback); fs.unlink(srcFile, callback);
}, },
function(callback) function(callback) {
{ // 100ms delay to accommodate for slow windows fs
//100ms delay to accomidate for slow windows fs if (os.type().indexOf("Windows") > -1) {
if(os.type().indexOf("Windows") > -1) setTimeout(function() {
{
setTimeout(function()
{
fs.unlink(destFile, callback); fs.unlink(destFile, callback);
}, 100); }, 100);
} } else {
else
{
fs.unlink(destFile, callback); fs.unlink(destFile, callback);
} }
} }
], callback); ], callback);
} }
], function(err) ],
{ function(err) {
if (err && err != "stop") ERR(err); if (err && err != "stop") ERR(err);
}) })
} }

View file

@ -37,11 +37,12 @@ var ERR = require("async-stacktrace")
var convertor = null; var convertor = null;
var exportExtension = "htm"; var exportExtension = "htm";
//load abiword only if its enabled and if soffice is disabled // load abiword only if it is enabled and if soffice is disabled
if(settings.abiword != null && settings.soffice === null) if (settings.abiword != null && settings.soffice === null) {
convertor = require("../utils/Abiword"); convertor = require("../utils/Abiword");
}
//load soffice only if its enabled // load soffice only if it is enabled
if (settings.soffice != null) { if (settings.soffice != null) {
convertor = require("../utils/LibreOffice"); convertor = require("../utils/LibreOffice");
exportExtension = "html"; exportExtension = "html";
@ -80,9 +81,11 @@ exports.doImport = function(req, res, padId)
form.uploadDir = tmpDirectory; form.uploadDir = tmpDirectory;
form.parse(req, function(err, fields, files) { form.parse(req, function(err, fields, files) {
//the upload failed, stop at this point
if (err || files.file === undefined) { if (err || files.file === undefined) {
if(err) console.warn("Uploading Error: " + err.stack); // the upload failed, stop at this point
if (err) {
console.warn("Uploading Error: " + err.stack);
}
callback("uploadFailed"); callback("uploadFailed");
return; return;
@ -119,18 +122,21 @@ exports.doImport = function(req, res, padId)
callback("uploadFailed"); callback("uploadFailed");
} }
}, },
function(callback) { function(callback) {
destFile = path.join(tmpDirectory, "etherpad_import_" + randNum + "." + exportExtension); destFile = path.join(tmpDirectory, "etherpad_import_" + randNum + "." + exportExtension);
// Logic for allowing external Import Plugins // Logic for allowing external Import Plugins
hooks.aCallAll("import", { srcFile: srcFile, destFile: destFile }, function(err, result) { hooks.aCallAll("import", { srcFile: srcFile, destFile: destFile }, function(err, result) {
if (ERR(err, callback)) return callback(); if (ERR(err, callback)) return callback();
if (result.length > 0) { // This feels hacky and wrong.. if (result.length > 0) { // This feels hacky and wrong..
importHandledByPlugin = true; importHandledByPlugin = true;
} }
callback(); callback();
}); });
}, },
function(callback) { function(callback) {
var fileEnding = path.extname(srcFile).toLowerCase() var fileEnding = path.extname(srcFile).toLowerCase()
var fileIsNotEtherpad = (fileEnding !== ".etherpad"); var fileIsNotEtherpad = (fileEnding !== ".etherpad");
@ -145,7 +151,7 @@ exports.doImport = function(req, res, padId)
padManager.getPad(padId, function(err, _pad) { padManager.getPad(padId, function(err, _pad) {
var headCount = _pad.head; var headCount = _pad.head;
if (headCount >= 10) { if (headCount >= 10) {
apiLogger.warn("Direct database Import attempt of a pad that already has content, we wont be doing this") apiLogger.warn("Direct database Import attempt of a pad that already has content, we wont be doing this");
return callback("padHasData"); return callback("padHasData");
} }
@ -157,7 +163,8 @@ exports.doImport = function(req, res, padId)
}); });
}); });
}, },
//convert file to html
// convert file to html if necessary
function(callback) { function(callback) {
if (importHandledByPlugin || directDatabaseAccess) { if (importHandledByPlugin || directDatabaseAccess) {
callback(); callback();
@ -168,7 +175,9 @@ exports.doImport = function(req, res, padId)
var fileEnding = path.extname(srcFile).toLowerCase(); var fileEnding = path.extname(srcFile).toLowerCase();
var fileIsHTML = (fileEnding === ".html" || fileEnding === ".htm"); var fileIsHTML = (fileEnding === ".html" || fileEnding === ".htm");
var fileIsTXT = (fileEnding === ".txt"); var fileIsTXT = (fileEnding === ".txt");
if (fileIsTXT) useConvertor = false; // Don't use convertor for text files if (fileIsTXT) useConvertor = false; // Don't use convertor for text files
// See https://github.com/ether/etherpad-lite/issues/2572 // See https://github.com/ether/etherpad-lite/issues/2572
if (fileIsHTML || (useConvertor === false)) { if (fileIsHTML || (useConvertor === false)) {
// if no convertor only rename // if no convertor only rename
@ -257,20 +266,23 @@ exports.doImport = function(req, res, padId)
var fileEnding = path.extname(srcFile).toLowerCase(); var fileEnding = path.extname(srcFile).toLowerCase();
if (importHandledByPlugin || useConvertor || fileEnding == ".htm" || fileEnding == ".html") { if (importHandledByPlugin || useConvertor || fileEnding == ".htm" || fileEnding == ".html") {
importHtml.setPadHTML(pad, text, function(e){ importHtml.setPadHTML(pad, text, function(e){
if(e) apiLogger.warn("Error importing, possibly caused by malformed HTML"); if (e) {
apiLogger.warn("Error importing, possibly caused by malformed HTML");
}
}); });
} else { } else {
pad.setText(text); pad.setText(text);
} }
} }
// Load the Pad into memory then brodcast updates to all clients // Load the Pad into memory then broadcast updates to all clients
padManager.unloadPad(padId); padManager.unloadPad(padId);
padManager.getPad(padId, function(err, _pad) { padManager.getPad(padId, function(err, _pad) {
var pad = _pad; var pad = _pad;
padManager.unloadPad(padId); padManager.unloadPad(padId);
// direct Database Access means a pad user should perform a switchToPad // direct Database Access means a pad user should perform a switchToPad
// and not attempt to recieve updated pad data.. // and not attempt to receive updated pad data
if (directDatabaseAccess) { if (directDatabaseAccess) {
callback(); callback();
@ -310,8 +322,7 @@ exports.doImport = function(req, res, padId)
var status = "ok"; var status = "ok";
// check for known errors and replace the status // check for known errors and replace the status
if(err == "uploadFailed" || err == "convertFailed" || err == "padHasData") if (err == "uploadFailed" || err == "convertFailed" || err == "padHasData") {
{
status = err; status = err;
err = null; err = null;
} }
@ -331,4 +342,3 @@ exports.doImport = function(req, res, padId)
); );
}); });
} }

File diff suppressed because it is too large Load diff

View file

@ -57,16 +57,15 @@ exports.setSocketIO = function(_socket) {
socket.sockets.on('connection', function(client) socket.sockets.on('connection', function(client)
{ {
// Broken: See http://stackoverflow.com/questions/4647348/send-message-to-specific-client-with-socket-io-and-node-js // Broken: See http://stackoverflow.com/questions/4647348/send-message-to-specific-client-with-socket-io-and-node-js
// Fixed by having a persistant object, ideally this would actually be in the database layer // Fixed by having a persistant object, ideally this would actually be in the database layer
// TODO move to database layer // TODO move to database layer
if (settings.trustProxy && client.handshake.headers['x-forwarded-for'] !== undefined) { if (settings.trustProxy && client.handshake.headers['x-forwarded-for'] !== undefined) {
remoteAddress[client.id] = client.handshake.headers['x-forwarded-for']; remoteAddress[client.id] = client.handshake.headers['x-forwarded-for'];
} } else {
else{
remoteAddress[client.id] = client.handshake.address; remoteAddress[client.id] = client.handshake.address;
} }
var clientAuthorized = false; var clientAuthorized = false;
// wrap the original send function to log the messages // wrap the original send function to log the messages
@ -81,28 +80,27 @@ exports.setSocketIO = function(_socket) {
components[i].handleConnect(client); components[i].handleConnect(client);
} }
client.on('message', function(message) client.on('message', function(message) {
{
if (message.protocolVersion && message.protocolVersion != 2) { if (message.protocolVersion && message.protocolVersion != 2) {
messageLogger.warn("Protocolversion header is not correct:" + stringifyWithoutPassword(message)); messageLogger.warn("Protocolversion header is not correct:" + stringifyWithoutPassword(message));
return; return;
} }
//client is authorized, everything ok
if (clientAuthorized) { if (clientAuthorized) {
// client is authorized, everything ok
handleMessage(client, message); handleMessage(client, message);
} else { //try to authorize the client } else {
// try to authorize the client
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) {
var checkAccessCallback = function(err, statusObject) { var checkAccessCallback = function(err, statusObject) {
ERR(err); ERR(err);
//access was granted, mark the client as authorized and handle the message
if (statusObject.accessStatus == "grant") { if (statusObject.accessStatus == "grant") {
// access was granted, mark the client as authorized and handle the message
clientAuthorized = true; clientAuthorized = true;
handleMessage(client, message); handleMessage(client, message);
} } else {
//no access, send the client a message that tell him why // no access, send the client a message that tells him why
else {
messageLogger.warn("Authentication try failed:" + stringifyWithoutPassword(message)); messageLogger.warn("Authentication try failed:" + stringifyWithoutPassword(message));
client.json.send({accessStatus: statusObject.accessStatus}); client.json.send({accessStatus: statusObject.accessStatus});
} }
@ -116,17 +114,16 @@ exports.setSocketIO = function(_socket) {
// this message has everything to try an authorization // this message has everything to try an authorization
securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, checkAccessCallback); securityManager.checkAccess (message.padId, message.sessionID, message.token, message.password, checkAccessCallback);
} }
} else { //drop message } else {
// drop message
messageLogger.warn("Dropped message cause of bad permissions:" + stringifyWithoutPassword(message)); messageLogger.warn("Dropped message cause of bad permissions:" + stringifyWithoutPassword(message));
} }
} }
}); });
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 components) {
{
components[i].handleDisconnect(client); components[i].handleDisconnect(client);
} }
}); });
@ -136,7 +133,6 @@ exports.setSocketIO = function(_socket) {
// try to handle the message of this client // try to handle the message of this client
function handleMessage(client, message) function handleMessage(client, message)
{ {
if (message.component && components[message.component]) { if (message.component && 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 (components[message.component]) {
@ -154,13 +150,13 @@ function stringifyWithoutPassword(message)
{ {
var newMessage = {}; var newMessage = {};
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);
} }

View file

@ -13,31 +13,32 @@ exports.expressCreateServer = function (hook_name, args, cb) {
search_results: {}, search_results: {},
errors: [], errors: [],
}; };
res.send(eejs.require("ep_etherpad-lite/templates/admin/plugins.html", render_args)); res.send(eejs.require("ep_etherpad-lite/templates/admin/plugins.html", render_args));
}); });
args.app.get('/admin/plugins/info', function(req, res) { args.app.get('/admin/plugins/info', function(req, res) {
var gitCommit = settings.getGitCommit(); var gitCommit = settings.getGitCommit();
var epVersion = settings.getEpVersion(); var epVersion = settings.getEpVersion();
res.send( eejs.require("ep_etherpad-lite/templates/admin/plugins-info.html",
{ res.send(eejs.require("ep_etherpad-lite/templates/admin/plugins-info.html", {
gitCommit: gitCommit, gitCommit: gitCommit,
epVersion: epVersion epVersion: epVersion
}) }));
);
}); });
} }
exports.socketio = function(hook_name, args, cb) { exports.socketio = function(hook_name, args, cb) {
var io = args.io.of("/pluginfw/installer"); var io = args.io.of("/pluginfw/installer");
io.on('connection', function(socket) { io.on('connection', function(socket) {
if (!socket.conn.request.session || !socket.conn.request.session.user || !socket.conn.request.session.user.is_admin) return; if (!socket.conn.request.session || !socket.conn.request.session.user || !socket.conn.request.session.user.is_admin) return;
socket.on("getInstalled", function(query) { socket.on("getInstalled", function(query) {
// send currently installed plugins // send currently installed plugins
var installed = Object.keys(plugins.plugins).map(function(plugin) { var installed = Object.keys(plugins.plugins).map(function(plugin) {
return plugins.plugins[plugin].package return plugins.plugins[plugin].package
}) });
socket.emit("results:installed", {installed: installed}); socket.emit("results:installed", {installed: installed});
}); });
@ -49,22 +50,27 @@ exports.socketio = function (hook_name, args, cb) {
socket.emit("results:updatable", {updatable: {}}); socket.emit("results:updatable", {updatable: {}});
return; return;
} }
var updatable = _(plugins.plugins).keys().filter(function(plugin) { var updatable = _(plugins.plugins).keys().filter(function(plugin) {
if (!results[plugin]) return false; if (!results[plugin]) return false;
var latestVersion = results[plugin].version
var currentVersion = plugins.plugins[plugin].package.version var latestVersion = results[plugin].version;
return semver.gt(latestVersion, currentVersion) var currentVersion = plugins.plugins[plugin].package.version;
return semver.gt(latestVersion, currentVersion);
}); });
socket.emit("results:updatable", {updatable: updatable}); socket.emit("results:updatable", {updatable: updatable});
}); });
}) });
socket.on("getAvailable", function(query) { socket.on("getAvailable", function(query) {
installer.getAvailablePlugins(/*maxCacheAge:*/ false, function(er, results) { installer.getAvailablePlugins(/*maxCacheAge:*/ false, function(er, results) {
if (er) { if (er) {
console.error(er) console.error(er);
results = {} results = {};
} }
socket.emit("results:available", results); socket.emit("results:available", results);
}); });
}); });
@ -72,15 +78,16 @@ exports.socketio = function (hook_name, args, cb) {
socket.on("search", function(query) { socket.on("search", function(query) {
installer.search(query.searchTerm, /*maxCacheAge:*/ 60 * 10, function(er, results) { installer.search(query.searchTerm, /*maxCacheAge:*/ 60 * 10, function(er, results) {
if (er) { if (er) {
console.error(er) console.error(er);
results = {} results = {};
} }
var res = Object.keys(results) var res = Object.keys(results)
.map(function(pluginName) { .map(function(pluginName) {
return results[pluginName] return results[pluginName];
}) })
.filter(function(plugin) { .filter(function(plugin) {
return !plugins.plugins[plugin.name] return !plugins.plugins[plugin.name];
}); });
res = sortPluginList(res, query.sortBy, query.sortDir) res = sortPluginList(res, query.sortBy, query.sortDir)
.slice(query.offset, query.offset+query.limit); .slice(query.offset, query.offset+query.limit);
@ -90,14 +97,16 @@ exports.socketio = function (hook_name, args, cb) {
socket.on("install", function(plugin_name) { socket.on("install", function(plugin_name) {
installer.install(plugin_name, function(er) { installer.install(plugin_name, function(er) {
if(er) console.warn(er) if (er) console.warn(er);
socket.emit("finished:install", {plugin: plugin_name, code: er? er.code : null, error: er? er.message : null}); socket.emit("finished:install", {plugin: plugin_name, code: er? er.code : null, error: er? er.message : null});
}); });
}); });
socket.on("uninstall", function(plugin_name) { socket.on("uninstall", function(plugin_name) {
installer.uninstall(plugin_name, function(er) { installer.uninstall(plugin_name, function(er) {
if(er) console.warn(er) if (er) console.warn(er);
socket.emit("finished:uninstall", {plugin: plugin_name, error: er? er.message : null}); socket.emit("finished:uninstall", {plugin: plugin_name, error: er? er.message : null});
}); });
}); });
@ -106,11 +115,15 @@ exports.socketio = function (hook_name, args, cb) {
function sortPluginList(plugins, property, /*ASC?*/dir) { function sortPluginList(plugins, property, /*ASC?*/dir) {
return plugins.sort(function(a, b) { return plugins.sort(function(a, b) {
if (a[property] < b[property]) if (a[property] < b[property]) {
return dir? -1 : 1; return dir? -1 : 1;
if (a[property] > b[property]) }
if (a[property] > b[property]) {
return dir? 1 : -1; return dir? 1 : -1;
}
// a must be equal to b // a must be equal to b
return 0; return 0;
}) });
} }

View file

@ -12,7 +12,10 @@ exports.gracefulShutdown = function(err) {
} }
// ensure there is only one graceful shutdown running // ensure there is only one graceful shutdown running
if(exports.onShutdown) return; if (exports.onShutdown) {
return;
}
exports.onShutdown = true; exports.onShutdown = true;
console.log("graceful shutdown..."); console.log("graceful shutdown...");
@ -42,7 +45,7 @@ exports.expressCreateServer = function (hook_name, args, cb) {
res.status(500).send({ error: 'Sorry, something bad happened!' }); res.status(500).send({ error: 'Sorry, something bad happened!' });
console.error(err.stack? err.stack : err.toString()); console.error(err.stack? err.stack : err.toString());
stats.meter('http500').mark() stats.meter('http500').mark()
}) });
/* /*
* Connect graceful shutdown with sigint and uncaught exception * Connect graceful shutdown with sigint and uncaught exception

View file

@ -24,8 +24,7 @@ exports.expressCreateServer = function (hook_name, args, cb) {
hasPadAccess(req, res, function() { hasPadAccess(req, res, function() {
console.log('req.params.pad', req.params.pad); console.log('req.params.pad', req.params.pad);
padManager.doesPadExists(req.params.pad, function(err, exists) padManager.doesPadExists(req.params.pad, function(err, exists) {
{
if (!exists) { if (!exists) {
return next(); return next();
} }
@ -38,8 +37,7 @@ exports.expressCreateServer = function (hook_name, args, cb) {
// handle import requests // handle import requests
args.app.post('/p/:pad/import', function(req, res, next) { args.app.post('/p/:pad/import', function(req, res, next) {
hasPadAccess(req, res, function() { hasPadAccess(req, res, function() {
padManager.doesPadExists(req.params.pad, function(err, exists) padManager.doesPadExists(req.params.pad, function(err, exists) {
{
if (!exists) { if (!exists) {
return next(); return next();
} }

View file

@ -6,17 +6,14 @@ var exporthtml = require("../../utils/ExportHtml");
exports.expressCreateServer = function (hook_name, args, cb) { exports.expressCreateServer = function (hook_name, args, cb) {
// serve read only pad // serve read only pad
args.app.get('/ro/:id', function(req, res) args.app.get('/ro/:id', function(req, res) {
{
var html; var html;
var padId; var padId;
async.series([ async.series([
// translate the read only pad to a padId // translate the read only pad to a padId
function(callback) function(callback) {
{ readOnlyManager.getPadId(req.params.id, function(err, _padId) {
readOnlyManager.getPadId(req.params.id, function(err, _padId)
{
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
padId = _padId; padId = _padId;
@ -28,28 +25,24 @@ exports.expressCreateServer = function (hook_name, args, cb) {
}); });
}, },
// render the html document // render the html document
function(callback) function(callback) {
{
// return if the there is no padId // return if the there is no padId
if(padId == null) if(padId == null) {
{
callback("notfound"); callback("notfound");
return; return;
} }
hasPadAccess(req, res, function() hasPadAccess(req, res, function() {
{
// render the html document // render the html document
exporthtml.getPadHTMLDocument(padId, null, function(err, _html) exporthtml.getPadHTMLDocument(padId, null, function(err, _html) {
{
if(ERR(err, callback)) return; if(ERR(err, callback)) return;
html = _html; html = _html;
callback(); callback();
}); });
}); });
} }
], function(err) ],
{ function(err) {
// throw any unexpected error // throw any unexpected error
if(err && err != "notfound") if(err && err != "notfound")
ERR(err); ERR(err);

View file

@ -2,29 +2,26 @@ var padManager = require('../../db/PadManager');
var url = require('url'); var url = require('url');
exports.expressCreateServer = function (hook_name, args, cb) { exports.expressCreateServer = function (hook_name, args, cb) {
// redirects browser to the pad's sanitized url if needed. otherwise, renders the html // redirects browser to the pad's sanitized url if needed. otherwise, renders the html
args.app.param('pad', function (req, res, next, padId) { args.app.param('pad', function (req, res, next, padId) {
// ensure the padname is valid and the url doesn't end with a / // ensure the padname is valid and the url doesn't end with a /
if(!padManager.isValidPadId(padId) || /\/$/.test(req.url)) if (!padManager.isValidPadId(padId) || /\/$/.test(req.url)) {
{
res.status(404).send('Such a padname is forbidden'); res.status(404).send('Such a padname is forbidden');
return; return;
} }
padManager.sanitizePadId(padId, function(sanitizedPadId) { padManager.sanitizePadId(padId, function(sanitizedPadId) {
if (sanitizedPadId != padId) {
// the pad id was sanitized, so we redirect to the sanitized version // the pad id was sanitized, so we redirect to the sanitized version
if(sanitizedPadId != padId)
{
var real_url = sanitizedPadId; var real_url = sanitizedPadId;
real_url = encodeURIComponent(real_url); real_url = encodeURIComponent(real_url);
var query = url.parse(req.url).query; var query = url.parse(req.url).query;
if ( query ) real_url += '?' + query; if ( query ) real_url += '?' + query;
res.header('Location', real_url); res.header('Location', real_url);
res.status(302).send('You should be redirected to <a href="' + real_url + '">' + real_url + '</a>'); res.status(302).send('You should be redirected to <a href="' + real_url + '">' + real_url + '</a>');
} } else {
// the pad id was fine, so just render it // the pad id was fine, so just render it
else
{
next(); next();
} }
}); });

View file

@ -5,7 +5,6 @@ var path = require("path")
exports.expressCreateServer = function (hook_name, args, cb) { exports.expressCreateServer = function (hook_name, args, cb) {
args.app.get('/tests/frontend/specs_list.js', function(req, res) { args.app.get('/tests/frontend/specs_list.js', function(req, res) {
async.parallel({ async.parallel({
coreSpecs: function(callback) { coreSpecs: function(callback) {
exports.getCoreTests(callback); exports.getCoreTests(callback);
@ -20,10 +19,8 @@ exports.expressCreateServer = function (hook_name, args, cb) {
console.debug("Sent browser the following test specs:", files.sort()); console.debug("Sent browser the following test specs:", files.sort());
res.send("var specs_list = " + JSON.stringify(files.sort()) + ";\n"); res.send("var specs_list = " + JSON.stringify(files.sort()) + ";\n");
}); });
}); });
// path.join seems to normalize by default, but we'll just be explicit // path.join seems to normalize by default, but we'll just be explicit
var rootTestFolder = path.normalize(path.join(npm.root, "../tests/frontend/")); var rootTestFolder = path.normalize(path.join(npm.root, "../tests/frontend/"));
@ -35,6 +32,7 @@ exports.expressCreateServer = function (hook_name, args, cb) {
subPath = subPath.split("?")[0]; subPath = subPath.split("?")[0];
var filePath = path.normalize(path.join(rootTestFolder, subPath)); var filePath = path.normalize(path.join(rootTestFolder, subPath));
// make sure we jail the paths to the test folder, otherwise serve index // make sure we jail the paths to the test folder, otherwise serve index
if (filePath.indexOf(rootTestFolder) !== 0) { if (filePath.indexOf(rootTestFolder) !== 0) {
filePath = path.join(rootTestFolder, "index.html"); filePath = path.join(rootTestFolder, "index.html");
@ -69,9 +67,11 @@ exports.getPluginTests = function(callback){
var pluginSpecs = []; var pluginSpecs = [];
var plugins = fs.readdirSync('node_modules'); var plugins = fs.readdirSync('node_modules');
plugins.forEach(function(plugin) { plugins.forEach(function(plugin) {
if(fs.existsSync("node_modules/"+plugin+"/static/tests/frontend/specs")){ // if plugins exists if (fs.existsSync("node_modules/" + plugin + "/static/tests/frontend/specs")) {
// if plugins exists
var specFiles = fs.readdirSync("node_modules/" + plugin + "/static/tests/frontend/specs/"); var specFiles = fs.readdirSync("node_modules/" + plugin + "/static/tests/frontend/specs/");
async.forEach(specFiles, function(spec){ // for each specFile push it to pluginSpecs async.forEach(specFiles, function(spec) {
// for each specFile push it to pluginSpecs
pluginSpecs.push("/static/plugins/" + plugin + "/static/tests/frontend/specs/" + spec); pluginSpecs.push("/static/plugins/" + plugin + "/static/tests/frontend/specs/" + spec);
}, },
function(err) { function(err) {
@ -83,9 +83,10 @@ exports.getPluginTests = function(callback){
} }
exports.getCoreTests = function(callback) { exports.getCoreTests = function(callback) {
fs.readdir('tests/frontend/specs', function(err, coreSpecs){ // get the core test specs // get the core test specs
fs.readdir('tests/frontend/specs', function(err, coreSpecs) {
if (err) { return res.send(500); } if (err) { return res.send(500); }
callback(null, coreSpecs); callback(null, coreSpecs);
}); });
} }

View file

@ -6,11 +6,11 @@ module.exports = function (req, res, callback) {
securityManager.checkAccess(req.params.pad, req.cookies.sessionID, req.cookies.token, req.cookies.password, function(err, accessObj) { securityManager.checkAccess(req.params.pad, req.cookies.sessionID, req.cookies.token, req.cookies.password, function(err, accessObj) {
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
//there is access, continue
if (accessObj.accessStatus == "grant") { if (accessObj.accessStatus == "grant") {
// there is access, continue
callback(); callback();
//no access
} else { } else {
// no access
res.status(403).send("403 - Can't touch this"); res.status(403).send("403 - Can't touch this");
} }
}); });

View file

@ -46,8 +46,8 @@ NodeVersion.enforceMinNodeVersion('8.9.0');
*/ */
var stats = require('./stats'); var stats = require('./stats');
stats.gauge('memoryUsage', function() { stats.gauge('memoryUsage', function() {
return process.memoryUsage().rss return process.memoryUsage().rss;
}) });
var settings var settings
, db , db
@ -59,8 +59,8 @@ async.waterfall([
// load npm // load npm
function(callback) { function(callback) {
npm.load({}, function(er) { npm.load({}, function(er) {
callback(er) callback(er);
}) });
}, },
// load everything // load everything
@ -74,13 +74,12 @@ async.waterfall([
}, },
// initalize the database // initalize the database
function (callback) function (callback) {
{
db.init(callback); db.init(callback);
}, },
function(callback) { function(callback) {
plugins.update(callback) plugins.update(callback);
}, },
function (callback) { function (callback) {
@ -94,8 +93,7 @@ async.waterfall([
}, },
// initalize the http server // initalize the http server
function (callback) function (callback) {
{
hooks.callAll("createServer", {}); hooks.callAll("createServer", {});
callback(null); callback(null);
} }

View file

@ -30,40 +30,32 @@ function getPadTXT(pad, revNum, callback)
var atext = pad.atext; var atext = pad.atext;
var html; var html;
async.waterfall([ async.waterfall([
// fetch revision atext // fetch revision atext
function(callback) {
if (revNum != undefined) {
function (callback) pad.getInternalRevisionAText(revNum, function(err, revisionAtext) {
{
if (revNum != undefined)
{
pad.getInternalRevisionAText(revNum, function (err, revisionAtext)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
atext = revisionAtext; atext = revisionAtext;
callback(); callback();
}); });
} } else {
else
{
callback(null); callback(null);
} }
}, },
// convert atext to html // convert atext to html
function(callback) {
// only this line is different to the HTML function
function (callback) html = getTXTFromAtext(pad, atext);
{
html = getTXTFromAtext(pad, atext); // only this line is different to the HTML function
callback(null); callback(null);
}], }],
// run final callback // run final callback
function(err) {
function (err)
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, html); callback(null, html);
}); });
} }
@ -80,17 +72,14 @@ function getTXTFromAtext(pad, atext, authorColors)
var anumMap = {}; var anumMap = {};
var css = ""; var css = "";
props.forEach(function (propName, i) props.forEach(function(propName, i) {
{
var propTrueNum = apool.putAttrib([propName, true], true); var propTrueNum = apool.putAttrib([propName, true], true);
if (propTrueNum >= 0) if (propTrueNum >= 0) {
{
anumMap[propTrueNum] = i; anumMap[propTrueNum] = i;
} }
}); });
function getLineTXT(text, attribs) function getLineTXT(text, attribs) {
{
var propVals = [false, false, false]; var propVals = [false, false, false];
var ENTER = 1; var ENTER = 1;
var STAY = 2; var STAY = 2;
@ -106,94 +95,77 @@ function getTXTFromAtext(pad, atext, authorColors)
var idx = 0; var idx = 0;
function processNextChars(numChars) function processNextChars(numChars) {
{ if (numChars <= 0) {
if (numChars <= 0)
{
return; return;
} }
var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars)); var iter = Changeset.opIterator(Changeset.subattribution(attribs, idx, idx + numChars));
idx += numChars; idx += numChars;
while (iter.hasNext()) while (iter.hasNext()) {
{
var o = iter.next(); var o = iter.next();
var propChanged = false; var propChanged = false;
Changeset.eachAttribNumber(o.attribs, function (a)
{ Changeset.eachAttribNumber(o.attribs, function(a) {
if (a in anumMap) if (a in anumMap) {
{
var i = anumMap[a]; // i = 0 => bold, etc. var i = anumMap[a]; // i = 0 => bold, etc.
if (!propVals[i])
{ if (!propVals[i]) {
propVals[i] = ENTER; propVals[i] = ENTER;
propChanged = true; propChanged = true;
} } else {
else
{
propVals[i] = STAY; propVals[i] = STAY;
} }
} }
}); });
for (var i = 0; i < propVals.length; i++)
{ for (var i = 0; i < propVals.length; i++) {
if (propVals[i] === true) if (propVals[i] === true) {
{
propVals[i] = LEAVE; propVals[i] = LEAVE;
propChanged = true; propChanged = true;
} } else if (propVals[i] === STAY) {
else if (propVals[i] === STAY) // set it back
{ propVals[i] = true;
propVals[i] = true; // set it back
} }
} }
// now each member of propVal is in {false,LEAVE,ENTER,true} // now each member of propVal is in {false,LEAVE,ENTER,true}
// according to what happens at start of span // according to what happens at start of span
if (propChanged) if (propChanged) {
{
// leaving bold (e.g.) also leaves italics, etc. // leaving bold (e.g.) also leaves italics, etc.
var left = false; var left = false;
for (var i = 0; i < propVals.length; i++)
{ for (var i = 0; i < propVals.length; i++) {
var v = propVals[i]; var v = propVals[i];
if (!left)
{ if (!left) {
if (v === LEAVE) if (v === LEAVE) {
{
left = true; left = true;
} }
} } else {
else if (v === true) {
{ // tag will be closed and re-opened
if (v === true) propVals[i] = STAY;
{
propVals[i] = STAY; // tag will be closed and re-opened
} }
} }
} }
var tags2close = []; var tags2close = [];
for (var i = propVals.length - 1; i >= 0; i--) for (var i = propVals.length - 1; i >= 0; i--) {
{ if (propVals[i] === LEAVE) {
if (propVals[i] === LEAVE)
{
//emitCloseTag(i); //emitCloseTag(i);
tags2close.push(i); tags2close.push(i);
propVals[i] = false; propVals[i] = false;
} } else if (propVals[i] === STAY) {
else if (propVals[i] === STAY)
{
//emitCloseTag(i); //emitCloseTag(i);
tags2close.push(i); tags2close.push(i);
} }
} }
for (var i = 0; i < propVals.length; i++) for (var i = 0; i < propVals.length; i++) {
{ if (propVals[i] === ENTER || propVals[i] === STAY) {
if (propVals[i] === ENTER || propVals[i] === STAY)
{
propVals[i] = true; propVals[i] = true;
} }
} }
@ -201,9 +173,9 @@ function getTXTFromAtext(pad, atext, authorColors)
} // end if (propChanged) } // end if (propChanged)
var chars = o.chars; var chars = o.chars;
if (o.lines) if (o.lines) {
{ // exclude newline at end of line, if present
chars--; // exclude newline at end of line, if present chars--;
} }
var s = taker.take(chars); var s = taker.take(chars);
@ -220,19 +192,19 @@ function getTXTFromAtext(pad, atext, authorColors)
} // end iteration over spans in line } // end iteration over spans in line
var tags2close = []; var tags2close = [];
for (var i = propVals.length - 1; i >= 0; i--) for (var i = propVals.length - 1; i >= 0; i--) {
{ if (propVals[i]) {
if (propVals[i])
{
tags2close.push(i); tags2close.push(i);
propVals[i] = false; propVals[i] = false;
} }
} }
} // end processNextChars } // end processNextChars
processNextChars(text.length - idx); processNextChars(text.length - idx);
return(assem.toString()); return(assem.toString());
} // end getLineHTML } // end getLineHTML
var pieces = [css]; var pieces = [css];
// Need to deal with constraints imposed on HTML lists; can // Need to deal with constraints imposed on HTML lists; can
@ -242,22 +214,25 @@ function getTXTFromAtext(pad, atext, authorColors)
// so we want to do something reasonable there. We also // so we want to do something reasonable there. We also
// want to deal gracefully with blank lines. // want to deal gracefully with blank lines.
// => keeps track of the parents level of indentation // => keeps track of the parents level of indentation
for (var i = 0; i < textLines.length; i++) for (var i = 0; i < textLines.length; i++) {
{
var line = _analyzeLine(textLines[i], attribLines[i], apool); var line = _analyzeLine(textLines[i], attribLines[i], apool);
var lineContent = getLineTXT(line.text, line.aline); var lineContent = getLineTXT(line.text, line.aline);
if (line.listTypeName == "bullet") { if (line.listTypeName == "bullet") {
lineContent = "* " + lineContent; // add a bullet lineContent = "* " + lineContent; // add a bullet
} }
if (line.listLevel > 0) { if (line.listLevel > 0) {
for (var j = line.listLevel - 1; j >= 0; j--) { for (var j = line.listLevel - 1; j >= 0; j--) {
pieces.push('\t'); pieces.push('\t');
} }
if (line.listTypeName == "number") { if (line.listTypeName == "number") {
pieces.push(line.listLevel + ". "); pieces.push(line.listLevel + ". ");
// This is bad because it doesn't truly reflect what the user // This is bad because it doesn't truly reflect what the user
// sees because browsers do magic on nested <ol><li>s // sees because browsers do magic on nested <ol><li>s
} }
pieces.push(lineContent, '\n'); pieces.push(lineContent, '\n');
} else { } else {
pieces.push(lineContent, '\n'); pieces.push(lineContent, '\n');
@ -266,17 +241,17 @@ function getTXTFromAtext(pad, atext, authorColors)
return pieces.join(''); return pieces.join('');
} }
exports.getTXTFromAtext = getTXTFromAtext; exports.getTXTFromAtext = getTXTFromAtext;
exports.getPadTXTDocument = function(padId, revNum, callback) exports.getPadTXTDocument = function(padId, revNum, callback)
{ {
padManager.getPad(padId, function (err, pad) padManager.getPad(padId, function(err, pad) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
getPadTXT(pad, revNum, function (err, html) getPadTXT(pad, revNum, function(err, html) {
{
if (ERR(err, callback)) return; if (ERR(err, callback)) return;
callback(null, html); callback(null, html);
}); });
}); });

View file

@ -18,26 +18,26 @@ var log4js = require('log4js');
var async = require("async"); var async = require("async");
var db = require("../db/DB").db; var db = require("../db/DB").db;
exports.setPadRaw = function(padId, records, callback){ exports.setPadRaw = function(padId, records, callback)
{
records = JSON.parse(records); records = JSON.parse(records);
async.eachSeries(Object.keys(records), function(key, cb) { async.eachSeries(Object.keys(records), function(key, cb) {
var value = records[key] var value = records[key];
if (!value) { if (!value) {
return setImmediate(cb); return setImmediate(cb);
} }
// Author data
if (value.padIDs) { if (value.padIDs) {
// rewrite author pad ids // Author data - rewrite author pad ids
value.padIDs[padId] = 1; value.padIDs[padId] = 1;
var newKey = key; var newKey = key;
// Does this author already exist? // Does this author already exist?
db.get(key, function(err, author) { db.get(key, function(err, author) {
if (author) { if (author) {
// Yes, add the padID to the author.. // Yes, add the padID to the author
if (Object.prototype.toString.call(author) === '[object Array]') { if (Object.prototype.toString.call(author) === '[object Array]') {
author.padIDs.push(padId); author.padIDs.push(padId);
} }
@ -47,28 +47,27 @@ exports.setPadRaw = function(padId, records, callback){
value.padIDs = [padId]; value.padIDs = [padId];
} }
}); });
// Not author data, probably pad data
} else { } else {
// we can split it to look to see if its pad data // Not author data, probably pad data
// we can split it to look to see if it's pad data
var oldPadId = key.split(":"); var oldPadId = key.split(":");
// we know its pad data.. // we know it's pad data
if (oldPadId[0] === "pad") { if (oldPadId[0] === "pad") {
// so set the new pad id for the author // so set the new pad id for the author
oldPadId[1] = padId; oldPadId[1] = padId;
// and create the value // and create the value
var newKey = oldPadId.join(":"); // create the new key var newKey = oldPadId.join(":"); // create the new key
} }
} }
// Write the value to the server // Write the value to the server
db.set(newKey, value); db.set(newKey, value);
setImmediate(cb); setImmediate(cb);
}, function(){ },
function() {
callback(null, true); callback(null, true);
}); });
} }

View file

@ -36,19 +36,22 @@ function setPadHTML(pad, html, callback)
// Convert a dom tree into a list of lines and attribute liens // Convert a dom tree into a list of lines and attribute liens
// using the content collector object // using the content collector object
var cc = contentcollector.makeContentCollector(true, null, pad.pool); var cc = contentcollector.makeContentCollector(true, null, pad.pool);
try{ // we use a try here because if the HTML is bad it will blow up try {
// we use a try here because if the HTML is bad it will blow up
cc.collectContent(doc); cc.collectContent(doc);
} catch(e) { } catch(e) {
apiLogger.warn("HTML was not properly formed", e); apiLogger.warn("HTML was not properly formed", e);
return callback(e); // We don't process the HTML because it was bad..
// don't process the HTML because it was bad
return callback(e);
} }
var result = cc.finish(); var result = cc.finish();
apiLogger.debug('Lines:'); apiLogger.debug('Lines:');
var i; var i;
for (i = 0; i < result.lines.length; i += 1) for (i = 0; i < result.lines.length; i += 1) {
{
apiLogger.debug('Line ' + (i + 1) + ' text: ' + result.lines[i]); apiLogger.debug('Line ' + (i + 1) + ' text: ' + result.lines[i]);
apiLogger.debug('Line ' + (i + 1) + ' attributes: ' + result.lineAttribs[i]); apiLogger.debug('Line ' + (i + 1) + ' attributes: ' + result.lineAttribs[i]);
} }
@ -59,18 +62,15 @@ function setPadHTML(pad, html, callback)
apiLogger.debug(newText); apiLogger.debug(newText);
var newAttribs = result.lineAttribs.join('|1+1') + '|1+1'; var newAttribs = result.lineAttribs.join('|1+1') + '|1+1';
function eachAttribRun(attribs, func /*(startInNewText, endInNewText, attribs)*/ ) function eachAttribRun(attribs, func /*(startInNewText, endInNewText, attribs)*/ ) {
{
var attribsIter = Changeset.opIterator(attribs); var attribsIter = Changeset.opIterator(attribs);
var textIndex = 0; var textIndex = 0;
var newTextStart = 0; var newTextStart = 0;
var newTextEnd = newText.length; var newTextEnd = newText.length;
while (attribsIter.hasNext()) while (attribsIter.hasNext()) {
{
var op = attribsIter.next(); var op = attribsIter.next();
var nextIndex = textIndex + op.chars; var nextIndex = textIndex + op.chars;
if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) if (!(nextIndex <= newTextStart || textIndex >= newTextEnd)) {
{
func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs); func(Math.max(newTextStart, textIndex), Math.min(newTextEnd, nextIndex), op.attribs);
} }
textIndex = nextIndex; textIndex = nextIndex;
@ -81,13 +81,13 @@ function setPadHTML(pad, html, callback)
var builder = Changeset.builder(1); var builder = Changeset.builder(1);
// assemble each line into the builder // assemble each line into the builder
eachAttribRun(newAttribs, function(start, end, attribs) eachAttribRun(newAttribs, function(start, end, attribs) {
{
builder.insert(newText.substring(start, end), attribs); builder.insert(newText.substring(start, end), attribs);
}); });
// the changeset is ready! // the changeset is ready!
var theChangeset = builder.toString(); var theChangeset = builder.toString();
apiLogger.debug('The changeset: ' + theChangeset); apiLogger.debug('The changeset: ' + theChangeset);
pad.setText("\n"); pad.setText("\n");
pad.appendRevision(theChangeset); pad.appendRevision(theChangeset);

View file

@ -4,15 +4,16 @@ var exportHtml = require('./ExportHtml');
function PadDiff (pad, fromRev, toRev) { function PadDiff (pad, fromRev, toRev) {
// check parameters // check parameters
if(!pad || !pad.id || !pad.atext || !pad.pool) if (!pad || !pad.id || !pad.atext || !pad.pool) {
{
throw new Error('Invalid pad'); throw new Error('Invalid pad');
} }
var range = pad.getValidRevisionRange(fromRev, toRev); var range = pad.getValidRevisionRange(fromRev, toRev);
if(!range) { throw new Error('Invalid revision range.' + if (!range) {
throw new Error('Invalid revision range.' +
' startRev: ' + fromRev + ' startRev: ' + fromRev +
' endRev: ' + toRev); } ' endRev: ' + toRev);
}
this._pad = pad; this._pad = pad;
this._fromRev = range.startRev; this._fromRev = range.startRev;
@ -26,12 +27,14 @@ PadDiff.prototype._isClearAuthorship = function(changeset){
var unpacked = Changeset.unpack(changeset); var unpacked = Changeset.unpack(changeset);
// check if there is nothing in the charBank // check if there is nothing in the charBank
if(unpacked.charBank !== "") if (unpacked.charBank !== "") {
return false; return false;
}
// check if oldLength == newLength // check if oldLength == newLength
if(unpacked.oldLen !== unpacked.newLen) if (unpacked.oldLen !== unpacked.newLen) {
return false; return false;
}
// lets iterator over the operators // lets iterator over the operators
var iterator = Changeset.opIterator(unpacked.ops); var iterator = Changeset.opIterator(unpacked.ops);
@ -40,17 +43,20 @@ PadDiff.prototype._isClearAuthorship = function(changeset){
var clearOperator = iterator.next(); var clearOperator = iterator.next();
// check if there is only one operator // check if there is only one operator
if(iterator.hasNext() === true) if (iterator.hasNext() === true) {
return false; return false;
}
// check if this operator doesn't change text // check if this operator doesn't change text
if(clearOperator.opcode !== "=") if (clearOperator.opcode !== "=") {
return false; return false;
}
// check that this operator applys to the complete text // check that this operator applys to the complete text
// if the text ends with a new line, its exactly one character less, else it has the same length // if the text ends with a new line, its exactly one character less, else it has the same length
if(clearOperator.chars !== unpacked.oldLen-1 && clearOperator.chars !== unpacked.oldLen) if (clearOperator.chars !== unpacked.oldLen-1 && clearOperator.chars !== unpacked.oldLen) {
return false; return false;
}
var attributes = []; var attributes = [];
Changeset.eachAttribNumber(changeset, function(attrNum) { Changeset.eachAttribNumber(changeset, function(attrNum) {
@ -58,14 +64,16 @@ PadDiff.prototype._isClearAuthorship = function(changeset){
}); });
// check that this changeset uses only one attribute // check that this changeset uses only one attribute
if(attributes.length !== 1) if (attributes.length !== 1) {
return false; return false;
}
var appliedAttribute = this._pad.pool.getAttrib(attributes[0]); var appliedAttribute = this._pad.pool.getAttrib(attributes[0]);
// check if the applied attribute is an anonymous author attribute // check if the applied attribute is an anonymous author attribute
if(appliedAttribute[0] !== "author" || appliedAttribute[1] !== "") if (appliedAttribute[0] !== "author" || appliedAttribute[1] !== "") {
return false; return false;
}
return true; return true;
}; };
@ -138,13 +146,15 @@ PadDiff.prototype._getChangesetsInBulk = function(startRev, count, callback) {
callback(); callback();
}); });
}, function(err){ },
function(err) {
callback(err, changesets, authors); callback(err, changesets, authors);
}); });
}; };
PadDiff.prototype._addAuthors = function(authors) { PadDiff.prototype._addAuthors = function(authors) {
var self = this; var self = this;
// add to array if not in the array // add to array if not in the array
authors.forEach(function(author) { authors.forEach(function(author) {
if (self._authors.indexOf(author) == -1) { if (self._authors.indexOf(author) == -1) {
@ -250,6 +260,7 @@ PadDiff.prototype.getHtml = function(callback){
callback(); callback();
}); });
}, },
// get the authorColor table // get the authorColor table
function(callback) { function(callback) {
self._pad.getAllAuthorColors(function(err, _authorColors) { self._pad.getAllAuthorColors(function(err, _authorColors) {
@ -261,13 +272,15 @@ PadDiff.prototype.getHtml = function(callback){
callback(); callback();
}); });
}, },
// convert the atext to html // convert the atext to html
function(callback) { function(callback) {
html = exportHtml.getHTMLFromAtext(self._pad, atext, authorColors); html = exportHtml.getHTMLFromAtext(self._pad, atext, authorColors);
self._html = html; self._html = html;
callback(); callback();
} }
], function(err){ ],
function(err) {
callback(err, html); callback(err, html);
}); });
}; };
@ -305,12 +318,11 @@ PadDiff.prototype._extendChangesetWithAuthor = function(changeset, author, apool
while(iterator.hasNext()) { while(iterator.hasNext()) {
var operator = iterator.next(); var operator = iterator.next();
//this is a delete operator, extend it with the author
if (operator.opcode === "-") { if (operator.opcode === "-") {
// this is a delete operator, extend it with the author
operator.attribs = attribs; operator.attribs = attribs;
} } else if (operator.opcode === "=" && operator.attribs) {
// this is operator changes only attributes, let's mark which author did that // this is operator changes only attributes, let's mark which author did that
else if(operator.opcode === "=" && operator.attribs){
operator.attribs+="*"+Changeset.numToString(authorAttrib); operator.attribs+="*"+Changeset.numToString(authorAttrib);
} }
@ -384,10 +396,13 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
curLineNextOp.chars = 0; curLineNextOp.chars = 0;
curLineOpIter = Changeset.opIterator(alines_get(curLine)); curLineOpIter = Changeset.opIterator(alines_get(curLine));
} }
if (!curLineNextOp.chars) { if (!curLineNextOp.chars) {
curLineOpIter.next(curLineNextOp); curLineOpIter.next(curLineNextOp);
} }
var charsToUse = Math.min(numChars, curLineNextOp.chars); var charsToUse = Math.min(numChars, curLineNextOp.chars);
func(charsToUse, curLineNextOp.attribs, charsToUse == curLineNextOp.chars && curLineNextOp.lines > 0); func(charsToUse, curLineNextOp.attribs, charsToUse == curLineNextOp.chars && curLineNextOp.lines > 0);
numChars -= charsToUse; numChars -= charsToUse;
curLineNextOp.chars -= charsToUse; curLineNextOp.chars -= charsToUse;
@ -421,6 +436,7 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
assem.append(firstString); assem.append(firstString);
var lineNum = curLine + 1; var lineNum = curLine + 1;
while (len < numChars) { while (len < numChars) {
var nextString = lines_get(lineNum); var nextString = lines_get(lineNum);
len += nextString.length; len += nextString.length;
@ -433,6 +449,7 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
function cachedStrFunc(func) { function cachedStrFunc(func) {
var cache = {}; var cache = {};
return function (s) { return function (s) {
if (!cache[s]) { if (!cache[s]) {
cache[s] = func(s); cache[s] = func(s);
@ -474,10 +491,12 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
var appliedKey = attribKeys[i]; var appliedKey = attribKeys[i];
var appliedValue = attribValues[i]; var appliedValue = attribValues[i];
var oldValue = Changeset.attribsAttributeValue(attribs, appliedKey, apool); var oldValue = Changeset.attribsAttributeValue(attribs, appliedKey, apool);
if (appliedValue != oldValue) { if (appliedValue != oldValue) {
backAttribs.push([appliedKey, oldValue]); backAttribs.push([appliedKey, oldValue]);
} }
} }
return Changeset.makeAttribsString('=', backAttribs, apool); return Changeset.makeAttribsString('=', backAttribs, apool);
}); });
@ -501,6 +520,7 @@ PadDiff.prototype._createDeletionChangeset = function(cs, startAText, apool) {
// get the text we want to procceed in this step // get the text we want to procceed in this step
var processText = textLeftToProcess.substr(0, lengthToProcess); var processText = textLeftToProcess.substr(0, lengthToProcess);
textLeftToProcess = textLeftToProcess.substr(lengthToProcess); textLeftToProcess = textLeftToProcess.substr(lengthToProcess);
if (lineBreak) { if (lineBreak) {

View file

@ -6,8 +6,10 @@ var request = require("request");
var npmIsLoaded = false; var npmIsLoaded = false;
var withNpm = function(npmfn) { var withNpm = function(npmfn) {
if (npmIsLoaded) return npmfn(); if (npmIsLoaded) return npmfn();
npm.load({}, function(er) { npm.load({}, function(er) {
if (er) return npmfn(er); if (er) return npmfn(er);
npmIsLoaded = true; npmIsLoaded = true;
npm.on("log", function(message) { npm.on("log", function(message) {
console.log('npm: ',message) console.log('npm: ',message)
@ -17,26 +19,33 @@ var withNpm = function (npmfn) {
} }
var tasks = 0 var tasks = 0
function wrapTaskCb(cb) { function wrapTaskCb(cb) {
tasks++ tasks++;
return function() { return function() {
cb && cb.apply(this, arguments); cb && cb.apply(this, arguments);
tasks--; tasks--;
if (tasks == 0) onAllTasksFinished(); if (tasks == 0) onAllTasksFinished();
} }
} }
function onAllTasksFinished() { function onAllTasksFinished() {
hooks.aCallAll("restartServer", {}, function() {}); hooks.aCallAll("restartServer", {}, function() {});
} }
exports.uninstall = function(plugin_name, cb) { exports.uninstall = function(plugin_name, cb) {
cb = wrapTaskCb(cb); cb = wrapTaskCb(cb);
withNpm(function(er) { withNpm(function(er) {
if (er) return cb && cb(er); if (er) return cb && cb(er);
npm.commands.uninstall([plugin_name], function(er) { npm.commands.uninstall([plugin_name], function(er) {
if (er) return cb && cb(er); if (er) return cb && cb(er);
hooks.aCallAll("pluginUninstall", {plugin_name: plugin_name}, function(er, data) { hooks.aCallAll("pluginUninstall", {plugin_name: plugin_name}, function(er, data) {
if (er) return cb(er); if (er) return cb(er);
plugins.update(cb); plugins.update(cb);
}); });
}); });
@ -44,13 +53,17 @@ exports.uninstall = function(plugin_name, cb) {
}; };
exports.install = function(plugin_name, cb) { exports.install = function(plugin_name, cb) {
cb = wrapTaskCb(cb) cb = wrapTaskCb(cb);
withNpm(function(er) { withNpm(function(er) {
if (er) return cb && cb(er); if (er) return cb && cb(er);
npm.commands.install([plugin_name], function(er) { npm.commands.install([plugin_name], function(er) {
if (er) return cb && cb(er); if (er) return cb && cb(er);
hooks.aCallAll("pluginInstall", {plugin_name: plugin_name}, function(er, data) { hooks.aCallAll("pluginInstall", {plugin_name: plugin_name}, function(er, data) {
if (er) return cb(er); if (er) return cb(er);
plugins.update(cb); plugins.update(cb);
}); });
}); });
@ -63,18 +76,22 @@ var cacheTimestamp = 0;
exports.getAvailablePlugins = function(maxCacheAge, cb) { exports.getAvailablePlugins = function(maxCacheAge, cb) {
request("https://static.etherpad.org/plugins.json", function(er, response, plugins){ request("https://static.etherpad.org/plugins.json", function(er, response, plugins){
if (er) return cb && cb(er); if (er) return cb && cb(er);
if (exports.availablePlugins && maxCacheAge && Math.round(+ new Date / 1000) - cacheTimestamp <= maxCacheAge) { if (exports.availablePlugins && maxCacheAge && Math.round(+ new Date / 1000) - cacheTimestamp <= maxCacheAge) {
return cb && cb(null, exports.availablePlugins) return cb && cb(null, exports.availablePlugins);
} }
try { try {
plugins = JSON.parse(plugins); plugins = JSON.parse(plugins);
} catch (err) { } catch (err) {
console.error('error parsing plugins.json:', err); console.error('error parsing plugins.json:', err);
plugins = []; plugins = [];
} }
exports.availablePlugins = plugins; exports.availablePlugins = plugins;
cacheTimestamp = Math.round(+ new Date / 1000); cacheTimestamp = Math.round(+ new Date / 1000);
cb && cb(null, plugins)
cb && cb(null, plugins);
}); });
}; };
@ -82,10 +99,15 @@ exports.getAvailablePlugins = function(maxCacheAge, cb) {
exports.search = function(searchTerm, maxCacheAge, cb) { exports.search = function(searchTerm, maxCacheAge, cb) {
exports.getAvailablePlugins(maxCacheAge, function(er, results) { exports.getAvailablePlugins(maxCacheAge, function(er, results) {
if (er) return cb && cb(er); if (er) return cb && cb(er);
var res = {}; var res = {};
if (searchTerm)
if (searchTerm) {
searchTerm = searchTerm.toLowerCase(); searchTerm = searchTerm.toLowerCase();
for (var pluginName in results) { // for every available plugin }
for (var pluginName in results) {
// for every available plugin
if (pluginName.indexOf(plugins.prefix) != 0) continue; // TODO: Also search in keywords here! if (pluginName.indexOf(plugins.prefix) != 0) continue; // TODO: Also search in keywords here!
if (searchTerm && !~results[pluginName].name.toLowerCase().indexOf(searchTerm) if (searchTerm && !~results[pluginName].name.toLowerCase().indexOf(searchTerm)
@ -94,10 +116,13 @@ exports.search = function(searchTerm, maxCacheAge, cb) {
if (typeof results[pluginName].description === "undefined") { if (typeof results[pluginName].description === "undefined") {
console.debug('plugin without Description: %s', results[pluginName].name); console.debug('plugin without Description: %s', results[pluginName].name);
} }
continue; continue;
} }
res[pluginName] = results[pluginName]; res[pluginName] = results[pluginName];
} }
cb && cb(null, res)
}) cb && cb(null, res);
});
}; };

View file

@ -55,6 +55,7 @@ exports.formatHooks = function (hook_set_name) {
exports.callInit = function (cb) { exports.callInit = function (cb) {
var hooks = require("./hooks"); var hooks = require("./hooks");
async.map( async.map(
Object.keys(exports.plugins), Object.keys(exports.plugins),
function (plugin_name, cb) { function (plugin_name, cb) {
@ -83,6 +84,7 @@ exports.update = function (cb) {
exports.getPackages(function (er, packages) { exports.getPackages(function (er, packages) {
var parts = []; var parts = [];
var plugins = {}; var plugins = {};
// Load plugin metadata ep.json // Load plugin metadata ep.json
async.forEach( async.forEach(
Object.keys(packages), Object.keys(packages),
@ -106,6 +108,7 @@ exports.getPackages = function (cb) {
var dir = path.resolve(npm.dir, '..'); var dir = path.resolve(npm.dir, '..');
readInstalled(dir, function (er, data) { readInstalled(dir, function (er, data) {
if (er) cb(er, null); if (er) cb(er, null);
var packages = {}; var packages = {};
function flatten(deps) { function flatten(deps) {
_.chain(deps).keys().each(function (name) { _.chain(deps).keys().each(function (name) {