text:
var ERR = require("ep_etherpad-lite/node_modules/async-stacktrace");
var express = require('express');
var eejs = require('ep_etherpad-lite/node/eejs');
var padManager = require('ep_etherpad-lite/node/db/PadManager');
var db = require('ep_etherpad-lite/node/db/DB').db;
var groupManager = require('ep_etherpad-lite/node/db/GroupManager');
var Changeset = require('ep_etherpad-lite/static/js/Changeset');
var mysql = require('mysql');
var email = require('emailjs');
var settings = require('ep_etherpad-lite/node/utils/Settings');
var authorManager = require('ep_etherpad-lite/node/db/AuthorManager');
var sessionManager = require('ep_etherpad-lite/node/db/SessionManager');
var formidable = require("formidable");
var crypto = require('crypto');
var eMailAuth = require(__dirname + '/email.json');
var pkg = require('./package.json');

var DEBUG_ENABLED = false;

/*
 *  Common Utility Functions
 */
var log = function (type, message) {
    if (typeof message == 'string') {
        if (type == 'error') {
            console.error(pkg.name + ': ' + message);
        } else if (type == 'debug') {
            if (DEBUG_ENABLED) {
                console.log('(debug) ' + pkg.name + ': ' + message);
            }
        } else {
            console.log(pkg.name + ': ' + message);
        }
    }
    else console.log(message);
};

var mySqlErrorHandler = function (err) {
    log('debug', 'mySqlErrorHandler');
    // TODO: Review error handling
    var msg;
    if (fileName in err && lineNumber in err) {
        msg = 'MySQLError in ' + err.fileName + ' line ' + err.lineNumber + ': ';
    } else {
        msg = 'MySQLError: ';
    }
    if (err.fatal) {
        msg += '(FATAL) ';
    }
    msg += err.message;
    log('error', msg);
};

var connectFkt = function (err) {
    if (err) {
        log('error', "failed connecting to database");
    } else {
        log('info', "connected");
    }
};

var encryptPassword = function (password, salt, cb) {
    var encrypted = crypto.createHmac('sha256', salt).update(password).digest('hex');
    cb(encrypted);
};

var USER_EXISTS = 'User already Exists';
var PASSWORD_WRONG = 'Passwords do not agree';

var NO_VALID_MAIL = 'No valid E-Mail';
var PW_EMPTY = 'Password is empty';

var dbAuth = settings.dbSettings;
var dbAuthParams = {
    host: dbAuth.host,
    user: dbAuth.user,
    password: dbAuth.password,
    database: dbAuth.database,
    insecureAuth: true,
    stringifyObjects: true
};

function getRandomNum(lbound, ubound) {
    return (Math.floor(Math.random() * (ubound - lbound)) + lbound);
}

function getRandomChar(number, lower, upper, other, extra) {
    var numberChars = '0123456789';
    var lowerChars = 'abcdefghijklmnopqrstuvwxyz';
    var upperChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    var otherChars = '`~!@#$%^&*()-_=+[{]}\\|;:\'",<.>/? ';
    var charSet = extra;
    if (number == true)
        charSet += numberChars;
    if (lower == true)
        charSet += lowerChars;
    if (upper == true)
        charSet += upperChars;
    if (other == true)
        charSet += otherChars;
    return charSet.charAt(getRandomNum(0, charSet.length));
}

function createSalt(cb) {
    var mylength = 10;
    var myextraChars = '';
    var myfirstNumber = true;
    var myfirstLower = true;
    var myfirstUpper = true;
    var myfirstOther = false;
    var mylatterNumber = true;
    var mylatterLower = true;
    var mylatterUpper = true;
    var mylatterOther = false;

    var rc = "";
    if (mylength > 0) {
        rc += getRandomChar(myfirstNumber, myfirstLower, myfirstUpper, myfirstOther, myextraChars);
    }
    for (var idx = 1; idx < mylength; ++idx) {
        rc += getRandomChar(mylatterNumber, mylatterLower, mylatterUpper, mylatterOther, myextraChars);
    }
    cb(rc);

}

function getPassword(cb) {
    var mylength = 40;
    var myextraChars = '';
    var myfirstNumber = true;
    var myfirstLower = true;
    var myfirstUpper = true;
    var myfirstOther = false;
    var mylatterNumber = true;
    var mylatterLower = true;
    var mylatterUpper = true;
    var mylatterOther = false;

    var rc = "";
    if (mylength > 0) {
        rc += getRandomChar(myfirstNumber, myfirstLower, myfirstUpper, myfirstOther, myextraChars);
    }
    for (var idx = 1; idx < mylength; ++idx) {
        rc += getRandomChar(mylatterNumber, mylatterLower, mylatterUpper, mylatterOther, myextraChars);
    }
    cb(rc);
}

var connection = mysql.createConnection(dbAuthParams);
var connection2 = mysql.createConnection(dbAuthParams);
connection.connect(connectFkt);
connection2.connect(connectFkt);

var userAuthenticated = function (req, cb) {
    log('debug', 'userAuthenticated');
    if (req.session.username && req.session.password && req.session.userId && req.session.email) {
        cb(true);
    } else {
        cb(false);
    }
};

var userAuthentication = function (username, password, cb) {
    log('debug', 'userAuthentication');
    var userSql = "Select * from User where User.name = ?";
    var sent = false;
    var userFound = false;
    var considered = false;
    var active = true;
    var queryUser = connection.query(userSql, [username]);
    queryUser.on('error', mySqlErrorHandler);
    queryUser.on('result', function (foundUser) {
        userFound = true;
        encryptPassword(password, foundUser.salt, function (encrypted) {
            if (foundUser.pwd == encrypted && foundUser.considered && foundUser.active) {
                sent = true;
                cb(true, foundUser, null, considered);
            } else if (!foundUser.active) {
                active = false;
            }
            else {
                considered = foundUser.considered;
            }
        });
    });
    queryUser.on('end', function () {
        if (!sent) {
            cb(false, null, userFound, considered, active);
        }
    });
};

function existValueInDatabase(sql, params, cb) {
    log('debug', 'existValueInDatabase');
    connection.query(sql, params, function (err, found) {
        if (err) {
            log('error', 'existValueInDatabase error, sql: ' + sql);
            cb(false);
        } else if (!found || found.length == 0) {
            cb(false);
        } else {
            cb(true);
        }
    });
}

function getOneValueSql(sql, params, cb) {
    log('debug', 'getOneValueSql');
    var qry = connection.query(sql, params, function (err, found) {
        if (err) {
            log('error', 'getOneValueSql error, sql: ' + sql);
            cb(false);
        } else if (!found || found.length == 0) {
            cb(false, null);
        } else {
            cb(true, found);
        }
    });
    qry.on('error', mySqlErrorHandler)
}

function updateSql(sqlUpdate, params, cb) {
    log('debug', 'updateSql');
    var updateQuery = connection.query(sqlUpdate, params);
    updateQuery.on('error', mySqlErrorHandler);
    updateQuery.on('end', function () {
        cb(true);
    });
}

function getAllSql(sql, params, cb) {
    log('debug', 'getAllSql');
    var allInstances = [];
    var queryInstances = connection.query(sql, params);
    queryInstances.on('error', mySqlErrorHandler);
    queryInstances.on('result', function (foundInstance) {
        connection.pause();
        allInstances.push(foundInstance);
        connection.resume();
    });
    queryInstances.on('end', function () {
        cb(allInstances);
    });
}

function getUser(userId, cb) {
    log('debug', 'getUser');
    var sql = "Select * from User where userID = ?";
    getOneValueSql(sql, [userId], cb);
}

function getGroup(groupId, cb) {
    log('debug', 'getGroup');
    var sql = "Select * from Groups where groupID = ?";
    getOneValueSql(sql, [groupId], cb);
}

function getUserGroup(groupId, userId, cb) {
    log('debug', 'getUserGroup');
    var sql = "Select * from UserGroup where groupID = ? and userID = ?";
    getOneValueSql(sql, [groupId, userId], cb);
}

function getEtherpadGroupFromNormalGroup(id, cb) {
    log('debug', 'getEtherpadGroupFromNormalGroup');
    var getMapperSql = "Select * from store where store.key = ?";
    var getMapperQuery = connection2.query(getMapperSql, ["mapper2group:" + id]);
    getMapperQuery.on('error', mySqlErrorHandler);
    getMapperQuery.on('end', function () {
        log('debug', 'getEtherpadGroupFromNormalGroup getMapperQuery end');
    });
    getMapperQuery.on('result', function (mapper) {
        log('debug', 'getEtherpadGroupFromNormalGroup getMapperQuery result');
        cb(mapper.value.replace(/"/g, ''));
    });
}

function getPadsOfGroup(id, padname, cb) {
    log('debug', 'getPadsOfGroup');
    var allPads = [];
    var allSql = "Select * from GroupPads where GroupPads.GroupID = ? and GroupPads.PadName like ?";
    var queryPads = connection.query(allSql, [id, "%" + padname + "%"]);
    queryPads.on('error', mySqlErrorHandler);
    queryPads.on('result', function (foundPads) {
        log('debug', 'getPadsOfGroup result');
        connection.pause();
        var pad = {};
        pad.name = foundPads.PadName;
        log('debug', 'padname ' + pad.name);
        if (pad.name != "") {
            getEtherpadGroupFromNormalGroup(id, function (group) {
                log('debug', 'getEtherpadGroupFromNormalGroup cb');
                padManager.getPad(group + "$" + pad.name, null, function (err, origPad) {
                    if (err) log('error', err);
                    pad.isProtected = origPad.isPasswordProtected();
                    origPad.getLastEdit(function (err, lastEdit) {
                        pad.lastedit = converterPad(lastEdit);
                        allPads.push(pad);
                        connection.resume();
                    });
                });
            });
        } else {
            connection.resume();
        }
    });
    queryPads.on('end', function () {
        cb(allPads);
    });
}

var converter = function (UNIX_timestamp) {
    var a = new Date(UNIX_timestamp);
    var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    var year = a.getFullYear();
    var month = months[a.getMonth()];
    var date = a.getDate();
    var hour = (( a.getHours() < 10) ? "0" : "") + a.getHours();
    var min = ((a.getMinutes() < 10) ? "0" : "") + a.getMinutes();
    var sec = ((a.getSeconds() < 10) ? "0" : "") + a.getSeconds();
    return date + '. ' + month + ' ' + year + ' ' + hour + ':' + min + ':' + sec;
};

var converterPad = function (UNIX_timestamp) {
    var a = new Date(UNIX_timestamp);
    var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
    var year = a.getFullYear();
    var month = months[a.getMonth()];
    var date = a.getDate();
    var hour = (( a.getHours() < 10) ? "0" : "") + a.getHours();
    var min = ((a.getMinutes() < 10) ? "0" : "") + a.getMinutes();
    return date + '. ' + month + ' ' + year + ' ' + hour + ':' + min + ' Uhr';
};

function addUserToEtherpad(userName, cb) {
    log('debug', 'addUserToEtherpad');
    authorManager.createAuthorIfNotExistsFor(userName, null, function (err, author) {
        if (err) {
            log('error', 'something went wrong while creating author');
            cb();
        } else {
            cb(author);
        }
    });
}

function addPadToEtherpad(padName, groupId, cb) {
    log('debug', 'addPadToEtherpad');
    getEtherpadGroupFromNormalGroup(groupId, function (group) {
        groupManager.createGroupPad(group, padName, function (err) {
            if (err) {
                log('error', 'something went wrong while adding a group pad');
                cb()
            } else {
                cb();
            }
        });
    });
}

function deleteGroupFromEtherpad(id, cb) {
    getEtherpadGroupFromNormalGroup(id, function (group) {
        groupManager.deleteGroup(group, function (err) {
            if (err) {
                log('error', 'Something went wrong while deleting group from etherpad');
                cb();
            } else {
                var sql = "DELETE FROM store where store.key = ?";
                var updateQuery = connection2.query(sql, ["mapper2group:" + id]);
                updateQuery.on('error', mySqlErrorHandler);
                updateQuery.on('end', function () {
                    cb();
                });
            }
        });
    });
}

var deletePadFromEtherpad = function (name, groupid, cb) {
    getEtherpadGroupFromNormalGroup(groupid, function (group) {
        padManager.removePad(group + "$" + name);
        cb();
    });
};

function sendError(error, res) {
    var data = {};
    data.success = false;
    data.error = error;
    log('error', error);
    res.send(data);
}

exports.expressCreateServer = function (hook_name, args, cb) {
    function notRegisteredUpdate(userid, groupid, email) {
        var userGroupSql = "INSERT INTO UserGroup VALUES(?, ?, 2)";
        updateSql(userGroupSql, [userid, groupid], function (success) {
            if (success) {
                var deleteNotRegisteredSql = "DELETE FROM NotRegisteredUsersGroups where groupID = ? and email = ?";
                updateSql(deleteNotRegisteredSql, [groupid, email], function (success) {
                });
            }
        });
    }

    function checkInvitations(email, userid, cb) {
        var userNotRegisteredSql = "select * from NotRegisteredUsersGroups where email = ?";
        var notRegistereds = [];
        var queryInstances = connection2.query(userNotRegisteredSql, [email]);
        queryInstances.on('error', mySqlErrorHandler);
        queryInstances.on('result', function (foundInstance) {
            connection2.pause();
            notRegistereds.push(foundInstance);
            connection2.resume();
        });
        queryInstances.on('end', function () {
            if (notRegistereds.length < 1) {
                cb();
            }
            for (var i = 0; i < notRegistereds.length; i++) {
                notRegisteredUpdate(userid, notRegistereds[i].groupID, email);
                if ((i + 1) == notRegistereds.length)
                    cb();
            }
        });
    }

    var registerUser = function (user, cb) {
        if (user.password != user.passwordrepeat) {
            cb(false, PASSWORD_WRONG);
            return false; // break execution early
        }

        var Ergebnis = user.email.toString().match(/[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+.[a-zA-Z]{2,4}/);
        if (Ergebnis == null) {
            cb(false, NO_VALID_MAIL);
            return false; // break execution early
        }
        if (user.password == "") {
            cb(false, PW_EMPTY);
            return false; // break execution early
        }
        var existUser = "SELECT * from User where User.name = ?";
        var retValue = existValueInDatabase(existUser, [user.email], function (exists) {
            if (exists) {
                cb(false, USER_EXISTS);
            } else {
                getPassword(function (consString) {
                    var msg = eMailAuth.registrationtext;
                    msg = msg.replace(/<url>/, user.location + "confirm/" + consString);
                    var message = {
                        text: msg,
                        from: eMailAuth.registrationfrom,
                        to: user.email + " <" + user.email + ">",
                        subject: eMailAuth.registrationsubject
                    };

                    var nodemailer = require('nodemailer');
                    var transport = nodemailer.createTransport("sendmail");
                    if (eMailAuth.smtp == "false")
                        transport.sendMail(message);
                    else {
                        emailserver.send(message, function (err) {
                            if (err) {
                                log('error', err);
                            }
                        });
                    }
                    createSalt(function (salt) {
                        encryptPassword(user.password, salt, function (encrypted) {
                            var addUserSql = "INSERT INTO User VALUES(null, ?,?, 0, 0, ?,?,?,1)";
                            var addUserQuery = connection.query(addUserSql, [user.email, encrypted, user.fullname, consString, salt]);
                            addUserQuery.on('error', mySqlErrorHandler);
                            addUserQuery.on('result', function (newUser) {
                                connection.pause();
                                checkInvitations(user.email, newUser.insertId, function () {
                                    addUserToEtherpad(newUser.insertId, function () {
                                        connection.resume();
                                    });
                                });

                            });
                            addUserQuery.on('end', function () {
                                cb(true, null);
                            });
                        });
                    });
                });
            }
            return exists;
        });
        return retValue; // return status of function call
    };

    var emailserver = email.server.connect({
        user: eMailAuth.user,
        password: eMailAuth.password,
        host: eMailAuth.host,
        port: eMailAuth.port,
        ssl: eMailAuth.ssl
    });

    args.app.get('/index.html', function (req, res) {
        userAuthenticated(req, function (authenticated) {
            if (authenticated) {
                res.redirect('home.html'); // ../home.html
            } else {
                var render_args = {
                    errors: [],
                    username: req.session.username
                };
                res.send(eejs
                    .require("ep_user_pad_frontend/templates/index.ejs",
                        render_args));
            }
        });
    });

    args.app.get('/help.html', function (req, res) {
        userAuthenticated(req, function (authenticated) {
            if (authenticated) {
                getUser(req.session.userId, function (found, currUser) {
                    var render_args;
                    if (currUser && currUser.length > 0) {
                        render_args = {
                            errors: [],
                            username: currUser[0].FullName
                        };
                        res.send(eejs
                            .require("ep_user_pad_frontend/templates/help.ejs",
                                render_args));
                    } else {
                        render_args = {
                            errors: [],
                            username: ""
                        };
                        res.send(eejs
                            .require("ep_user_pad_frontend/templates/help.ejs",
                                render_args));
                    }
                });

            } else {
                res.redirect("../index.html");
            }
        });
    });

    args.app.get('/home.html', function (req, res) {
        userAuthenticated(req, function (authenticated) {
            if (authenticated) {
                var sql = "Select Groups.* from Groups inner join UserGroup on(UserGroup.groupID = Groups.groupID) where UserGroup.userID = ?";
                getAllSql(sql, [req.session.userId], function (groups) {
                    getUser(req.session.userId, function (found, currUser) {
                        var render_args;
                        if (currUser) {
                            render_args = {
                                errors: [],
                                username: currUser[0].FullName,
                                groups: groups
                            };
                            res.send(eejs
                                .require("ep_user_pad_frontend/templates/home.ejs",
                                    render_args));
                        } else {
                            render_args = {
                                errors: [],
                                username: " ",
                                groups: groups
                            };
                            res.send(eejs
                                .require("ep_user_pad_frontend/templates/home.ejs",
                                    render_args));
                        }
                    });
                });
            } else {
                res.redirect("../index.html");
            }
        });
    });

    args.app.get('/pads.html', function (req, res) {
        userAuthenticated(req, function (authenticated) {
            if (authenticated) {
                getUser(req.session.userId, function (found, currUser) {
                    if (currUser) {
                        var render_args = {
                            errors: [],
                            username: currUser[0].FullName
                        };
                        res.send(eejs
                            .require("ep_user_pad_frontend/templates/pads.ejs",
                                render_args));
                    }
                });
            } else {
                res.redirect("../index.html");
            }
        });
    });

    args.app.get('/group.html/:groupid', function (req, res) {
        log('debug', req.path);
        userAuthenticated(req, function (authenticated) {
            log('debug', req.path + 'userAuthenticated CB');
            if (authenticated) {
                log('debug', req.path + 'Authenticated');
                getPadsOfGroup(req.params.groupid, '', function (pads) {
                    log('debug', req.path + 'getPadsOfGroup CB');
                    getUser(req.session.userId, function (found, currUser) {
                        log('debug', req.path + 'getUser CB');
                        getGroup(req.params.groupid, function (found, currGroup) {
                            log('debug', req.path + 'getGroup CB');
                            getUserGroup(req.params.groupid, req.session.userId, function (found, currUserGroup) {
                                log('debug', req.path + 'getUserGroup CB');
                                var render_args;
                                if (!currUserGroup) {
                                    render_args = {
                                        errors: [],
                                        msg: "This group does not exist! Perhaps someone has deleted the group."
                                    };
                                    res.send(eejs
                                        .require("ep_user_pad_frontend/templates/msgtemplate.ejs",
                                            render_args));
                                    return;
                                }
                                var isown = currUserGroup[0].Role == 1;
                                if (currGroup && currUser && currUserGroup) {
                                    render_args = {
                                        errors: [],
                                        id: currGroup[0].name,
                                        groupid: currGroup[0].groupID,
                                        username: currUser[0].FullName,
                                        isowner: isown,
                                        pads: pads
                                    };
                                    res.send(eejs.require("ep_user_pad_frontend/templates/group.ejs", render_args));
                                } else {
                                    render_args = {
                                        errors: [],
                                        msg: "This group does not exist! Perhabs anyone has deleted the group."
                                    };
                                    res.send(eejs.require("ep_user_pad_frontend/templates/msgtemplate.ejs",
                                        render_args));
                                }
                            });
                        });
                    });

                });
            } else {
                res.redirect("../../index.html");
            }
        });
    });

    args.app.get('/groups.html', function (req, res) {
        userAuthenticated(req, function (authenticated) {
            if (authenticated) {
                var sql = "Select * from Groups inner join UserGroup on(UserGroup.groupID = Groups.groupID) where UserGroup.userID = ?";
                getAllSql(sql, [req.session.userId], function (groups) {
                    getUser(req.session.userId, function (found, currUser) {
                        var render_args = {
                            errors: [],
                            username: currUser[0].FullName,
                            groups: groups
                        };

                        res.send(eejs.require("ep_user_pad_frontend/templates/groups.ejs", render_args));
                    });

                });
            } else {
                res.redirect("../index.html");
            }
        });
    });

    args.app.post('/padSearchTerm', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    var data = {};
                    if (!fields.groupId) {
                        var error = 'Group Id undefined';
                        sendError(error, res);
                        return;
                    }
                    getPadsOfGroup(fields.groupId, fields.searchterm, function (pads) {
                        getUser(req.session.userId, function (found, currUser) {
                            getGroup(fields.groupId, function (found, currGroup) {
                                if (currUser && currGroup) {
                                    var noresults = pads.length <= 0;
                                    var render_args = {
                                        errors: [],
                                        noresults: noresults,
                                        pads: pads,
                                        id: currGroup[0].name,
                                        groupid: currGroup[0].groupID,
                                        username: currUser[0].FullName
                                    };
                                    data.success = true;
                                    data.html = eejs.require("ep_user_pad_frontend/templates/padtables.ejs", render_args);
                                    res.send(data);
                                }
                            });
                        });
                    });

                } else {
                    res.send("You are not logged in!!");
                }

            });
        });
    });

    args.app.post('/groupsSearchTerm', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    var sql = "Select * from Groups inner join UserGroup on(UserGroup.groupID = Groups.groupID) where UserGroup.userID = ? and Groups.name like ?";
                    getAllSql(sql, [req.session.userId, "%" + fields.searchterm + "%"], function (groups) {
                        var render_args = {
                            errors: [],
                            groups: groups
                        };
                        res.send(eejs.require("ep_user_pad_frontend/templates/grouptables.ejs", render_args));
                    });

                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });

    function inviteRegistered(email, inviter, location, userID, groupID, res) {
        var getGroupSql = "select * from Groups where Groups.groupID = ?";
        getAllSql(getGroupSql, [groupID], function (group) {
            var msg = eMailAuth.invitationmsg;
            msg = msg.replace(/<groupname>/, group[0].name);
            msg = msg.replace(/<fromuser>/, inviter);
            msg = msg.replace(/<url>/, location);
            var message = {
                text: msg,
                from: eMailAuth.invitationfrom,
                to: email + " <" + email + ">",
                subject: eMailAuth.invitationsubject
            };
            if (eMailAuth.smtp == "false") {
                var nodemailer = require('nodemailer');
                var transport = nodemailer.createTransport("sendmail");
                transport.sendMail(message);
            }
            else {
                emailserver.send(message, function (err) {
                    if (err) {
                        log('error', err);
                    }
                });
            }

            var existGroupSql = "select * from UserGroup where userID = ? and groupID = ?";
            getOneValueSql(existGroupSql, [userID, groupID], function (found) {
                if (found) {
                    sendError('One ore more user are already in Group', res);
                } else {
                    var sqlInsert = "INSERT INTO UserGroup Values(?,?,2)";
                    var insertQuery = connection.query(sqlInsert, [userID, groupID]);
                    insertQuery.on('error', mySqlErrorHandler);
                    insertQuery.on('end', function () {
                    });
                }
            });
        });
    }

    function inviteUnregistered(groupID, name, location, email, res) {
        var getGroupSql = "select * from Groups where Groups.groupID = ?";
        var queryInstances = connection.query(getGroupSql, [groupID]);
        queryInstances.on('error', mySqlErrorHandler);
        queryInstances.on('result', function (group) {
            var msg = eMailAuth.invitateunregisterednmsg;
            msg = msg.replace(/<groupname>/, group.name);
            msg = msg.replace(/<fromuser>/, name);
            msg = msg.replace(/<url>/, location);
            var message = {
                text: msg,
                from: eMailAuth.invitationfrom,
                to: email + " <" + email + ">",
                subject: eMailAuth.invitationsubject
            };
            if (eMailAuth.smtp == "false") {
                var nodemailer = require('nodemailer');
                var transport = nodemailer.createTransport("sendmail");
                transport.sendMail(message);
            }
            else {
                emailserver.send(message, function (err) {
                    if (err) {
                        log('error', err);
                    }
                });
            }
            var existGroupSql = "select * from NotRegisteredUsersGroups where email = ? and groupID = ?";
            getOneValueSql(existGroupSql, [email, groupID], function (found) {
                if (found) {
                    sendError('One ore more user are already Invited to this Group', res);
                } else {
                    var sqlInsert = "INSERT INTO NotRegisteredUsersGroups Values(?,?)";
                    var insertQuery = connection2.query(sqlInsert, [email, groupID]);
                    insertQuery.on('error', mySqlErrorHandler);
                    insertQuery.on('end', function () {
                    });
                }
            });
        });
    }

    function inviteUser(userN, location, groupID, res, currUserName) {
        var getUserSql = "select * from User where User.name = ?";
        getAllSql(getUserSql, [userN], function (user) {
            if (user[0] != null && user[0] != undefined && user.length > 0) {
                inviteRegistered(user[0].name, currUserName, location, user[0].userID, groupID, res);
            } else {
                inviteUnregistered(groupID, currUserName, location, userN, res);
            }
        });
    }

    args.app.post('/inviteUsers', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    if (!fields.groupID) {
                        sendError('Group ID not defined', res);
                        return;
                    } else if (!fields.users[0]) {
                        sendError('No User given', res);
                        return;
                    }
                    var isOwnerSql = "SELECT * from UserGroup where UserGroup.userId = ? and UserGroup.groupID= ?";
                    getAllSql(isOwnerSql, [req.session.userId, fields.groupID], function (userGroup) {
                        if (!(userGroup[0].Role == 1)) {
                            sendError('User is not Owner can not send Invitations', res);
                        } else {
                            var data = {};
                            data.success = true;
                            getUser(req.session.userId, function (found, currUser) {
                                for (var i = 0; i < fields.users.length; i++) {
                                    if (fields.users[i] != "") {
                                        var userEmail = fields.users[i].toString().replace(/\s/g, '');
                                        inviteUser(userEmail, fields.location, fields.groupID, res, currUser[0].FullName);
                                    }
                                }
                                res.send(data);
                            });
                        }
                    });
                } else {
                    sendError("You are not logged in!", res);
                }
            });
        });
    });

    args.app.post('/userSearchTerm', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    if (!fields.groupID) {
                        sendError('Group Id undefined', res);
                        return;
                    }
                    var isOwnerSql = "SELECT * from UserGroup where UserGroup.userId = ? and UserGroup.groupID= ?";
                    getAllSql(isOwnerSql, [req.session.userId, fields.groupID], function () {
                        var usersSql = "select User.name, User.FullName, User.userID, UserGroup.Role from User inner join UserGroup on(UserGroup.userID = User.userID) where User.userID not in (?) and User.userID in (select UserGroup.userID from UserGroup where groupID = ?) and UserGroup.groupID = ? and User.name like ?";
                        getAllSql(usersSql, [req.session.userId, fields.groupID, fields.groupID, "%" + fields.searchterm + "%"], function (users) {
                            var userNotRegisteredSql = "Select * from NotRegisteredUsersGroups where groupID = ?";
                            getAllSql(userNotRegisteredSql, [fields.groupID], function (notRegistereds) {
                                for (var i = 0; i < notRegistereds.length; i++) {
                                    var user = {};
                                    user.name = notRegistereds[i].email;
                                    user.notRegistered = true;
                                    users.push(user);
                                }
                                var data = {};
                                data.success = true;
                                data.users = users;
                                res.send(data);
                            });
                        });
                    });
                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });

    args.app.post('/deleteNotRegUser', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    var deleteNotRegisteredSql = "DELETE FROM NotRegisteredUsersGroups where groupID = ? and email = ?";
                    updateSql(deleteNotRegisteredSql, [fields.groupID, fields.username], function (success) {
                        var data = {};
                        data.success = success;
                        res.send(data);
                    });
                } else {
                    res.send("You are not logged in!!");
                }
            });
        });

    });

    args.app.post('/reinviteUser', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    getGroup(fields.groupID, function (found, currGroup) {
                        getUser(req.session.userId, function (found, currUser) {
                            var msg = eMailAuth.invitateunregisterednmsg;
                            msg = msg.replace(/<groupname>/, currGroup[0].name);
                            msg = msg.replace(/<fromuser>/, currUser[0].name);
                            msg = msg.replace(/<url>/, fields.location);
                            var message = {
                                text: msg,
                                from: eMailAuth.invitationfrom,
                                to: fields.username + " <" + fields.username + ">",
                                subject: eMailAuth.invitationsubject
                            };
                            if (eMailAuth.smtp == "false") {
                                var nodemailer = require('nodemailer');
                                var transport = nodemailer.createTransport("sendmail");
                                transport.sendMail(message);
                            }
                            else {
                                emailserver.send(message, function (err) {
                                    if (err) {
                                        log('error', err);
                                    }
                                });
                            }
                            var data = {};
                            data.success = true;
                            res.send(data);
                        });
                    });
                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });

    args.app.post('/changeUserName', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    if (fields.newUserName == "") {
                        sendError('User Name empty', res);
                        return;
                    }
                    var updateUserSql = "UPDATE User SET FullName = ? WHERE userID= ?";
                    var data = {};
                    updateSql(updateUserSql, [fields.newUserName, req.session.userId], function (success) {
                        data.success = success;
                        res.send(data);
                    });

                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });

    args.app.post('/changeEmail', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    if (fields.newEmail == "") {
                        sendError('Mail empty', res);
                        return;
                    }
                    var Ergebnis = fields.newEmail.toString().match(/[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+.[a-zA-Z]{2,4}/);
                    if (!Ergebnis) {
                        sendError(NO_VALID_MAIL, res);
                        return;
                    }
                    var updateUserSql = "UPDATE User SET name = ? WHERE userID= ?";
                    var data = {};
                    updateSql(updateUserSql, [fields.newEmail, req.session.userId], function (success) {
                        data.success = success;
                        res.send(data);
                    });

                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });

    args.app.post('/changeUserPw', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    if (fields.newPW == "" || fields.oldPW == "") {
                        sendError('Password empty', res);
                        return;
                    }
                    getUser(req.session.userId, function (found, currUser) {
                        if (currUser) {
                            encryptPassword(fields.oldPW, currUser[0].salt, function (encrypted) {
                                if (currUser[0].pwd != encrypted) {
                                    sendError('Wrong Password', res);
                                    return;
                                }
                                createSalt(function (salt) {
                                    encryptPassword(fields.newPW, salt, function (newPass) {
                                        var updateUserSql = "UPDATE User SET pwd = ?, salt = ? WHERE userID= ?";
                                        var data = {};
                                        updateSql(updateUserSql, [newPass, salt, req.session.userId], function (success) {
                                            data.success = success;
                                            res.send(data);
                                        });
                                    });
                                });
                            });
                        }
                    });

                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });

    args.app.post('/makeOwner', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    if (!fields.userID || fields.userID == "" || !fields.groupID || fields.groupID == "") {
                        sendError('No User ID or Group ID given', res);
                        return false;
                    }
                    var isOwnerSql = "SELECT * from UserGroup where UserGroup.userId = ? and UserGroup.groupID= ?";
                    var retCode = getAllSql(isOwnerSql, [req.session.userId, fields.groupID], function (userGroup) {
                        if (!(userGroup[0].Role == 1)) {
                            sendError('User is not owner! Can not delete Pad', res);
                            return false;
                        } else {
                            var updateUserSql = "UPDATE UserGroup SET Role = 1 WHERE userID= ? and groupID = ?";
                            var data = {};
                            updateSql(updateUserSql, [fields.userID, fields.groupID], function (success) {
                                if (success) {
                                    var updateOldUserSql = "UPDATE UserGroup SET Role = 2 WHERE userID = ? and groupID = ?";
                                    updateSql(updateOldUserSql, [req.session.userId, fields.groupID], function (success) {
                                        if (!success) {
                                            log('error', 'SetRole = 2 failed after SetRole = 1 succeded');
                                        }
                                        data.success = success;
                                        res.send(data);
                                    });
                                    return true;
                                } else {
                                    return false;
                                }

                            });
                            return true;
                        }
                    });
                    return retCode;
                } else {
                    log('info', 'Not logged in Error: ' + req.path);
                    res.send("You are not logged in!!");
                    return false;
                }
            });
        });
    });

    args.app.post('/deleteUserFromGroup', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    if (!fields.userID || fields.userID == "" || !fields.groupID || fields.groupID == "") {
                        sendError('No User ID or Group ID given', res);
                    } else {
                        var isOwnerSql = "SELECT * from UserGroup where UserGroup.userId = ? and UserGroup.groupID= ?";
                        getAllSql(isOwnerSql, [req.session.userId, fields.groupID], function (userGroup) {
                            if (!(userGroup[0].Role == 1)) {
                                sendError('You are not the owner of this group!!', res);
                                return false;
                            } else {
                                var deleteUserFromGroupSql = "Delete from UserGroup where userID = ? and groupID = ?";
                                var data = {};
                                updateSql(deleteUserFromGroupSql, [fields.userID, fields.groupID], function (success) {
                                    data.success = success;
                                    res.send(data);
                                });
                                return true;
                            }
                        });
                    }
                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });

    args.app.post('/deleteUser', function (req, res) {
        userAuthenticated(req, function (authenticated) {
            if (authenticated) {
                var isOwnerAnywhere = "select * from UserGroup where userID = ? and UserGroup.Role = 1";
                getOneValueSql(isOwnerAnywhere, [req.session.userId], function (found, UserGroup) {
                    if (found) {
                        sendError('You are owner in one ore more groups. Please select a follower for the ownership', res);
                        return false;
                    } else {
                        var deleteSql = "Delete from UserGroup where userID = ?";
                        var data = {};
                        updateSql(deleteSql, [req.session.userId], function (success) {
                            if (!success) {
                                res.send(data);
                                return;
                            }
                            var deleteSqlUser = "Delete from User where userID = ?";
                            updateSql(deleteSqlUser, [req.session.userId], function (success2) {
                                if (!success2) {
                                    res.end(data);
                                    return;
                                }
                                req.session.email = null;
                                req.session.password = null;
                                req.session.userId = null;
                                req.session.username = null;
                                data.success = success;
                                res.send(data);
                            });
                        });
                        return true;
                    }
                });
            } else {
                res.send("You are not logged in!!");
            }
        });
    });

    args.app.post('/directToPad', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    if (!fields.groupId) {
                        sendError('Group-Id not defined', res);
                        return;
                    }
                    var userInGroupSql = "SELECT * from UserGroup where UserGroup.userId = ? and UserGroup.groupID= ?";
                    getOneValueSql(userInGroupSql, [req.session.userId, fields.groupId], function (found) {
                        if (found) {
                            getEtherpadGroupFromNormalGroup(fields.groupId, function (group) {
                                addUserToEtherpad(req.session.userId, function (etherpad_author) {
                                    sessionManager.createSession(group, etherpad_author.authorID, Date.now() +
                                        7200000, function (err, session) {
                                        var data = {};
                                        data.success = true;
                                        data.session = session.sessionID;
                                        data.group = group;
                                        data.pad_name = fields.padname;
                                        data.location = fields.location;
                                        res.send(data);
                                    });
                                });
                            });
                        } else {
                            sendError('User not in Group', res);
                        }
                    });

                } else {
                    res.send("You are not logged in!!");

                }
            });
        });
    });

    args.app.get('/group.html/:groupID/pad.html/:padID', function (req, res) {
        userAuthenticated(req, function (authenticated) {
            getGroup(req.params.groupID, function (found, currGroup) {
                getUser(req.session.userId, function (found, currUser) {
                    var padID = req.params.padID;
                    var slice = padID.indexOf("$");
                    padID = padID.slice(slice + 1, padID.length);
                    var padsql = "select * from GroupPads where PadName = ?";
                    existValueInDatabase(padsql, [padID], function (found) {
                        var render_args;
                        if (!found) {
                            render_args = {
                                errors: [],
                                msg: "This pad does not exist! Perhabs anyone has deleted the pad."
                            };
                            res.send(eejs
                                .require("ep_user_pad_frontend/templates/msgtemplate.ejs",
                                    render_args));
                        } else {
                            if (currGroup && currGroup.length > 0) {
                                if (authenticated) {
                                    if (currUser) {
                                        render_args = {
                                            errors: [],
                                            padname: padID,
                                            username: currUser[0].FullName,
                                            groupID: req.params.groupID,
                                            groupName: currGroup[0].name,
                                            padurl: req.session.baseurl + "p/" + req.params.padID
                                        };
                                        res.send(eejs
                                            .require("ep_user_pad_frontend/templates/pad.ejs",
                                                render_args));
                                    } else
                                        res.send("Error");
                                } else {
                                    render_args = {
                                        errors: [],
                                        padname: req.params.padID,
                                        username: req.session.username,
                                        groupID: req.params.groupID,
                                        groupName: currGroup[0].name,
                                        padurl: req.session.baseurl + "p/" + req.params.padID
                                    };
                                    res.send(eejs
                                        .require("ep_user_pad_frontend/templates/pad_with_login.ejs",
                                            render_args));
                                }
                            } else {
                                render_args = {
                                    errors: [],
                                    msg: "This group does not exist! Perhabs anyone has deleted the group."
                                };
                                res.send(eejs
                                    .require("ep_user_pad_frontend/templates/msgtemplate.ejs",
                                        render_args));
                            }
                        }
                    });
                });
            });
        });
    });

    args.app.get('/imprint.html', function (req, res) {
        var render_args = {
            errors: []
        };
        res.send(eejs.require("ep_user_pad_frontend/templates/imprint.html", render_args));
    });

    args.app.get('/public_pad/:id', function (req, res) {
        userAuthenticated(req, function (authenticated) {
            var render_args;
            if (authenticated) {
                render_args = {
                    errors: [],
                    padurl: "p/" + req.params.id,
                    username: req.session.username,
                    padName: req.params.id
                };
                res.send(eejs
                    .require("ep_user_pad_frontend/templates/public_pad_logged_in.ejs",
                        render_args));
            } else {
                render_args = {
                    errors: [],
                    padurl: "./../p/" + req.params.id
                };
                res.send(eejs
                    .require("ep_user_pad_frontend/templates/public_pad.ejs",
                        render_args));
            }
        });
    });

    args.app.post('/login', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            if (!fields.email) {
                sendError('No valid E-mail Address given', res);
                return false;
            } else if (!fields.password) {
                sendError('No password given', res);
                return false;
            }

            var email = fields.email;
            var password = fields.password;

            var retVal = userAuthentication(email, password, function (success, user, userFound, considered, active) {
                if (success) {
                    req.session.email = email;
                    req.session.password = password;
                    req.session.userId = user.userID;
                    req.session.username = user.FullName;
                    req.session.baseurl = fields.url;
                    var data = {};
                    data.success = true;
                    res.send(data);
                    return true;
                } else {
                    if (!active) {
                        sendError('User is inactive', res);
                    }
                    else if (!userFound || considered) {
                        sendError('User or password wrong!', res);
                    }
                    else if (userFound && !considered) {
                        sendError('You have to confirm your registration!', res);
                    }
                    return false;
                }
            });
            return retVal;
        });
    });

    args.app.post('/register', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            var user = {};
            user.fullname = fields.fullname;
            user.email = fields.email;
            user.password = fields.password;
            user.passwordrepeat = fields.passwordrepeat;
            user.location = fields.location;
            registerUser(user, function (success, error) {
                if (!success) {
                    sendError(error, res);
                } else {
                    var data = {};
                    data.success = success;
                    data.error = error;
                    res.send(data);
                }
            });
        });
    });

    args.app.post('/logout', function (req, res) {
        req.session.email = null;
        req.session.password = null;
        req.session.userId = null;
        req.session.username = null;
        res.send(true);
    });

    args.app.get('/confirm/:consString', function (req, res) {
        var sql = "Select * from User where User.considerationString = ?";
        getOneValueSql(sql, [req.params.consString], function (found, user) {
            var render_args;
            if (!found) {
                render_args = {
                    errors: [],
                    msg: "User not found!"
                };
                res.send(eejs
                    .require("ep_user_pad_frontend/templates/msgtemplate.ejs",
                        render_args));
            } else {
                if (user[0].considered) {
                    render_args = {
                        errors: [],
                        msg: 'User already confirmed!'
                    };
                    res.send(eejs
                        .require("ep_user_pad_frontend/templates/msgtemplate.ejs",
                            render_args));
                } else {
                    var sql2 = "Update User SET considered = 1 WHERE User.userID = ?";
                    updateSql(sql2, [user[0].userID], function (success) {
                        if (success) {
                            var render_args = {
                                errors: [],
                                msg: 'Thanks for your registration!'
                            };
                            res.send(eejs
                                .require("ep_user_pad_frontend/templates/msgtemplate.ejs",
                                    render_args));
                        } else {
                            res.send('Something went wrong');
                        }
                    });
                }
            }
        });
    });

    args.app.post('/getUser', function (req, res) {
        userAuthenticated(req, function (authenticated) {
            if (authenticated) {
                getUser(req.session.userId, function (found, currUser) {
                    var data = {};
                    data.success = true;
                    data.user = currUser;
                    res.send(data);
                });
            } else {
                res.send("You are not logged in!!");
            }
        });
    });

    args.app.post('/createGroup', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                var data = {};
                if (authenticated) {
                    if (!fields.groupName) {
                        sendError("Group Name not defined", res);
                        return;
                    }
                    var existGroupSql = "SELECT * from Groups WHERE Groups.name = ?";
                    getOneValueSql(existGroupSql, [fields.groupName], function (found) {
                        if (found) {
                            sendError('Group already exists', res);
                            return;
                        } else {
                            var addGroupSql = "INSERT INTO Groups VALUES(null, ?)";
                            var addGroupQuery = connection.query(addGroupSql, [fields.groupName]);
                            addGroupQuery.on('error', mySqlErrorHandler);
                            addGroupQuery.on('result', function (group) {
                                data.groupid = group.insertId;
                                connection.pause();
                                var addUserGroupSql = "INSERT INTO UserGroup Values(?,?,1)";
                                var addUserGroupQuery = connection2.query(addUserGroupSql, [req.session.userId, group.insertId]);
                                addUserGroupQuery.on('error', mySqlErrorHandler);
                                addUserGroupQuery.on('result', function () {
                                    groupManager.createGroupIfNotExistsFor(group.insertId.toString(), function (err, val) {
                                        if (err) {
                                            log('error', 'failed to createGroupIfNotExistsFor');
                                        }
                                    });
                                });
                                addUserGroupQuery.on('end', function() {
                                    connection.resume();
                                })
                            });
                        }
                        addGroupQuery.on('end', function () {
                            data.success = true;
                            data.error = null;
                            res.send(data);
                        });
                    });
                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });

    args.app.post('/createPad', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            if (err) {
                log('error', 'formidable parsing error in ' + req.path);
                res.send(err);
                return;
            }
            userAuthenticated(req, function (authenticated) {
                if (authenticated) {
                    if (!fields.groupId) {
                        sendError('Group-Id not defined', res);
                        return;
                    } else if (!fields.padName) {
                        sendError('Pad Name not defined', res);
                        return;
                    }
                    var existPadInGroupSql = "SELECT * from GroupPads where GroupPads.GroupID = ? and GroupPads.PadName = ?";
                    getOneValueSql(existPadInGroupSql, [fields.groupId, fields.padName], function (found) {
                        if (found || (fields.padName.length == 0)) {
                            sendError('Pad already Exists', res);
                        } else {
                            var addPadToGroupSql = "INSERT INTO GroupPads VALUES(?, ?)";
                            var addPadToGroupQuery = connection.query(addPadToGroupSql, [fields.groupId, fields.padName]);
                            addPadToGroupQuery.on('error', mySqlErrorHandler);
                            addPadToGroupQuery.on('end', function () {
                                addPadToEtherpad(fields.padName, fields.groupId, function () {
                                    var data = {};
                                    data.success = true;
                                    data.error = null;
                                    res.send(data);
                                });
                            });
                        }
                    });
                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });

    args.app.post('/deletePad', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                var data = {};
                if (authenticated) {
                    if (!fields.groupId) {
                        sendError('Group-Id not defined', res);
                        return;
                    } else if (!fields.padName) {
                        sendError('Pad Name not defined', res);
                        return;
                    }
                    var isOwnerSql = "SELECT * from UserGroup where UserGroup.userId = ? and UserGroup.groupID= ?";
                    getAllSql(isOwnerSql, [req.session.userId, fields.groupId], function (userGroup) {
                        if (!(userGroup[0].Role == 1)) {
                            sendError('User is not owner! Can not delete Pad', res);
                        } else {
                            getEtherpadGroupFromNormalGroup(fields.groupId, function () {
                                var deletePadSql = "DELETE FROM GroupPads WHERE GroupPads.PadName = ? and GroupPads.GroupID = ?";
                                var deletePadQuery = connection.query(deletePadSql, [fields.padName, fields.groupId]);
                                deletePadQuery.on('error', mySqlErrorHandler);
                                deletePadQuery.on('result', function (pad) {
                                });
                                deletePadQuery.on('end', function () {
                                    deletePadFromEtherpad(fields.padName, fields.groupId, function () {
                                        data.success = true;
                                        data.error = null;
                                        res.send(data);
                                    });

                                });
                            });
                        }
                    });
                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });

    args.app.post('/deleteGroup', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                var data = {};
                if (authenticated) {
                    if (!fields.groupId) {
                        sendError('Group-Id not defined', res);
                        return;
                    }
                    var isOwnerSql = "SELECT * from UserGroup where UserGroup.userId = ? and UserGroup.groupID= ?";
                    getAllSql(isOwnerSql, [req.session.userId, fields.groupId], function (userGroup) {
                        if (!userGroup) {
                            sendError('You are not in this Group.', res);
                        }
                        if (!(userGroup[0].Role == 1)) {
                            sendError('User is not Owner. Can not delete Group', res);
                        } else {
                            var deleteGroupSql = "DELETE FROM Groups WHERE Groups.groupID = ?";
                            var deleteGroupQuery = connection.query(deleteGroupSql, [fields.groupId]);
                            deleteGroupQuery.on('error', mySqlErrorHandler);
                            deleteGroupQuery.on('result', function () {
                                connection.pause();
                                var deleteUserGroupSql = "DELETE FROM UserGroup where UserGroup.groupID = ?";
                                var deleteUserGroupQuery = connection2.query(deleteUserGroupSql, [fields.groupId]);
                                deleteUserGroupQuery.on('error', mySqlErrorHandler);
                                deleteUserGroupQuery.on('end', function () {
                                    var deleteGroupPadsSql = "DELETE FROM GroupPads where GroupPads.groupID = ?";
                                    var deleteGroupPadsQuery = connection2.query(deleteGroupPadsSql, [fields.groupId]);
                                    deleteGroupPadsQuery.on('error', mySqlErrorHandler);
                                    deleteGroupPadsQuery.on('end', function () {
                                        var deleteGroupNotInvited = "DELETE FROM NotRegisteredUsersGroups where NotRegisteredUsersGroups.groupID = ?";
                                        var deleteGroupNotInvitedQuery = connection2.query(deleteGroupNotInvited, [fields.groupId]);
                                        deleteGroupNotInvitedQuery.on('error', mySqlErrorHandler);
                                        deleteGroupNotInvitedQuery.on('end', function () {
                                            deleteGroupFromEtherpad(fields.groupId, function () {
                                                connection.resume();
                                            });
                                        });
                                    });
                                });
                            });
                            deleteGroupQuery.on('end', function () {
                                data.success = true;
                                data.error = null;
                                res.send(data);
                            });
                        }

                    });
                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });

    args.app.post('/setPassword', function (req, res) {
        new formidable.IncomingForm().parse(req, function (err, fields) {
            userAuthenticated(req, function (authenticated) {
                var data = {};
                if (authenticated) {
                    if (!fields.groupId) {
                        sendError('Group-Id not defined', res);
                        return;
                    }
                    var isOwnerSql = "SELECT * from UserGroup where UserGroup.userId = ? and UserGroup.groupID= ?";
                    getAllSql(isOwnerSql, [req.session.userId, fields.groupId], function (userGroup) {
                        if (!(userGroup[0].Role == 1) || fields.pw == '') {
                            sendError('User is not owner! Can not set Password', res);
                            return false;
                        } else {
                            getEtherpadGroupFromNormalGroup(fields.groupId, function (group) {
                                padManager.getPad(group + "$" + fields.padName, null, function (err, origPad) {
                                    if (err) {
                                        log('error', err);
                                        return false;
                                    }
                                    origPad.setPassword(fields.pw);
                                    data.success = true;
                                    data.error = null;
                                    res.send(data);
                                    return true;
                                });
                            });
                            return true;
                        }
                    });
                } else {
                    res.send("You are not logged in!!");
                }
            });
        });
    });
    return cb();

};
exports.eejsBlock_indexWrapper = function (hook_name, args, cb) {
    args.content = eejs
        .require("ep_user_pad_frontend/templates/index_redirect.ejs");
    return cb();
};

exports.eejsBlock_styles = function (hook_name, args, cb) {
    return cb();
};

redpajama_set_name: RedPajamaGithub


text:
Justia Forms Business Contracts KAR Auction Services, Inc. Amendment No. 1 to Eighth Amended and Restated Receivables Purchase
Amendment No. 1 to Eighth Amended and Restated Receivables Purchase Agreement, dated March 23, 2020, among AFC Funding Corporation, Automotive Finance Corporation, the entities from time to time parties hereto as Purchasers or Purchaser Agents and Bank of Montreal
Contract Categories: Business Finance - Purchase Agreements
EX-10.15B 4 exhibit1015b-amndmntno.htm EXHIBIT 10.15B - AMENDMENT NO. 1 TO 8TH A&R RPA Exhibit
EXHIBIT 10.15b
Execution Version
AMENDMENT NO. 1 TO EIGHTH AMENDED AND RESTATED
RECEIVABLES PURCHASE AGREEMENT
THIS AMENDMENT NO. 1 to EIGHTH AMENDED AND RESTATED RECEIVABLES PURCHASE AGREEMENT (this "Amendment"), dated as of March 23, 2020, is entered into among AFC FUNDING CORPORATION, an Indiana corporation (the "Seller"), AUTOMOTIVE FINANCE CORPORATION, an Indiana corporation (the "Servicer"), the Purchasers and Purchaser Agents signatories hereto, and BANK OF MONTREAL, as the agent (the "Agent").
R E C I T A L S
A. The Seller, the Servicer, the Purchasers, the Purchaser Agents, and the Agent are parties to that certain Eighth Amended and Restated Receivables Purchase Agreement dated as of December 18, 2018 (as amended, amended and restated, supplemented or otherwise modified prior to the date hereof, the "Agreement").
B. Pursuant to and in accordance with Section 6.1 of the Agreement, the Seller, the Servicer, the Purchasers, the Purchaser Agents and the Agent desire to amend the Agreement as hereinafter set forth.
NOW THEREFORE, for good and valuable consideration, the receipt and sufficiency of which are hereby acknowledged, the parties agree as follows:
1.Certain Defined Terms. Capitalized terms which are used herein without definition and that are defined in the Agreement shall have the same meanings herein as in the Agreement.
2. Amendment to Agreement. The Agreement is hereby amended as follows:
2.1. Each of the parties hereto agrees that any Receivable which complies with Schedule I - Curtailment Deferral Plan hereto (subject to the requirements specified in such schedule) shall constitute an Eligible Receivable notwithstanding that such Receivable (x) does not comply with clauses (n) or (o) of the definition of Eligible Receivable or (y) would otherwise constitute a Receivable described in clause (viii) of the definition of Special Concentration Percentage, in each case, solely to the extent such Receivable participates in such Curtailment Deferral Plan.
2.2. Clause (b)(3) of Section 1.4 of the Agreement (Allocation of the Seller's Share of the Collections Prior to Termination Date) is amended as follows:
(3) third, (i) until each of the Purchasers otherwise consents in writing, either, as specified by the Seller or Servicer, (x) to be retained in the Deposit Accounts or Liquidation Account until the following Business Day for distribution pursuant to Section 1.4(b) or 1.4(e), as applicable, on such following Business Day or (y) to be distributed to repay the Investment of the Participation of the Purchasers pursuant to Section 1.4(f) or (ii) following receipt of consent in writing from each of the Purchasers, to the Seller.
2.3. Clause (c)(10) of Section 1.4 of the Agreement (Daily Purchaser Share Allocation) is amended as follows:
(10) tenth, (i) until each of the Purchasers otherwise consents in writing, either, as specified by the Seller or Servicer, (x) to be retained in the Deposit Accounts or Liquidation Account until the following Business Day for distribution pursuant to Section 1.4(c) or 1.4(e), as applicable, on such following Business Day or (y) to be distributed to repay the Investment of the Participation of the Purchasers pursuant to Section 1.4(f) or (ii) following receipt of consent in writing from each of the Purchasers, to the Seller but only to the extent no Paydown Day exists or would result from such distribution.
2.4. Each of the parties hereto agrees that, until consented to in writing by each of the Purchasers, only obligations described in clauses (i) (overnight obligations of the United States of America) and (ii) (time deposits maintained at financial institutions rated at the time of investment not less than A-1 by S&P and P-1 by Moody's) shall constitute Permitted Investments.
2.5. Clause (o) of Exhibit IV is amended to add the following to the beginning of such clause: "Notwithstanding the following, no Restricted Payments shall be permitted to be made by the Seller following the execution of Amendment No.1 of this Agreement until each of the Purchasers consents in writing thereto."
3. Representations and Warranties. Each of the Seller and the Servicer hereby represents and warrants to the Agent, the Purchasers and the Purchaser Agents as follows:
(a) Representations and Warranties. The representations and warranties of such Person contained in Exhibit III and Exhibit VII to the Agreement are true and correct as of the date hereof (unless stated to relate solely to an earlier date, in which case such representations and warranties were true and correct as of such earlier date).
(b) Enforceability. The execution and delivery by such Person of this Amendment, and the performance of its obligations under this Amendment and the Agreement, as amended hereby, are within its corporate powers and have been duly authorized by all necessary corporate action on its part. This Amendment and the Agreement, as amended hereby, are its valid and legally binding obligations, enforceable in accordance with its terms.
(c) Termination Event. No Termination Event or Unmatured Termination Event has occurred and is continuing.
4. Effectiveness. This Amendment shall become effective upon the receipt by the Agent of each of the counterparts of this Amendment executed by each of the parties hereto.
5. Effect of Amendment. Except as expressly amended and modified by this Amendment, all provisions of the Agreement shall remain in full force and effect. After this Amendment becomes effective, all references in the Agreement (or in any other Transaction Document) to "the Receivables Purchase Agreement," "this Agreement," "hereof," "herein" or
words of similar effect, in each case referring to the Agreement, shall be deemed to be references to the Agreement as amended by this Amendment. This Amendment shall not be deemed to expressly or impliedly waive, amend or supplement any provision of the Agreement other than as set forth herein.
6. Counterparts. This Amendment may be executed in any number of counterparts and by different parties on separate counterparts, and each counterpart shall be deemed to be an original, and all such counterparts shall together constitute but one and the same instrument. Delivery of an executed counterpart of a signature page of this Amendment by telecopy, emailed pdf. or any other electronic means that reproduces an image of the actual executed signature page shall be effective as delivery of a manually executed counterpart of this Amendment. The words "execution," "signed," "signature," "delivery," and words of like import in or relating to any document to be signed in connection with this Amendment and the transactions contemplated hereby shall be deemed to include Electronic Signatures, deliveries or the keeping of records in electronic form, each of which shall be of the same legal effect, validity or enforceability as a manually executed signature, physical delivery thereof or the use of a paper-based recordkeeping system, as the case may be, to the extent and as provided for in any applicable law, including the Federal Electronic Signatures in Global and National Commerce Act, the New York State Electronic Signatures and Records Act, or any other similar state laws based on the Uniform Electronic Transactions Act; provided that nothing herein shall require the Agent to accept electronic signatures in any form or format without its prior written consent. "Electronic Signature" means an electronic sound, symbol, or process attached to, or associated with, a contract or other record and adopted by a Person with the intent to sign, authenticate or accept such contract or record.
7. Governing Law. This Amendment shall be governed by, and construed in accordance with, the internal laws of the State of Indiana without reference to conflict of laws principles.
8. Section Headings. The various headings of this Amendment are inserted for convenience only and shall not affect the meaning or interpretation of this Amendment or the Agreement or any provision hereof or thereof.
9. Reaffirmation of Performance Guaranty. By signing below, KAR Auction Services, Inc. reaffirms its obligations under the Performance Guaranty after giving effect to this Amendment.
[SIGNATURE PAGES FOLLOW]
IN WITNESS WHEREOF, the parties have executed this Amendment as of the date first above written.
AFC FUNDING CORPORATION, as Seller
By: /s/ Amy Wirges
Name: Amy Wirges
Title: Senior VP of Finance & Treasurer
AUTOMOTIVE FINANCE CORPORATION,
as Servicer
Amendment No. 1 to Eighth A&R RPA
FAIRWAY FINANCE COMPANY, LLC, as a Purchaser
By: /s/ Irina Khaimova
Name: Irina Khaimova
Title: Vice President
BMO CAPITAL MARKETS CORP., as Purchaser
Agent for Fairway Finance Company, LLC and Bank of Montreal
By: /s/ John Pappano
Name: John Pappano
Title: Managing Director
BANK OF MONTREAL, as Agent
By: /s/ Karen Louie
Name: Karen Louie
BANK OF MONTREAL, as Purchaser
ROYAL BANK OF CANADA, as Purchaser Agent for Thunder Bay Funding, LLC
By: /s/ Kevin P. Wilson
Name: Kevin P. Wilson
Title: Authorized Signatory
By: /s/ Lisa Wang
Name: Lisa Wang
THUNDER BAY FUNDING, LLC, as a Purchaser
By: Royal Bank of Canada,
its attorney-in-fact
PNC BANK, NATIONAL ASSOCIATION,
as Purchaser and Purchaser Agent for itself
By: /s/ Lawrence Beller
Name: Lawrence Beller
Title: Senior Vice President
TRUIST BANK (as successor to SunTrust Bank), as Purchaser and as Purchaser Agent for itself
By: /s/ Ileana Chu
Name: Ileana Chu
Title: SVP
FIFTH THIRD BANK, NATIONAL ASSOCIATION, as Purchaser and as Purchaser Agent for itself
By: /s/ Brian Gardner
Name: Brian Gardner
JPMORGAN CHASE BANK N.A., as Purchaser Agent for Chariot Funding LLC
By: /s/ Cameron Milligan
Name: Cameron Milligan
CHARIOT FUNDING LLC, as a Purchaser
By: JPMorgan Chase Bank, N.A., its attorney-in-
Acknowledged and Agreed:
KAR AUCTION SERVICES, INC.,
as provider of the Performance Guaranty
By: /s/ Eric M. Loughmiller
Name: Eric M. Loughmiller
Title: EVP & Chief Financial Officer
Schedule I- Curtailment Deferral Plan
In an effort to assist customers who are experiencing cash flow issues due to the impact of COVID-19, AFC will be offering a Curtailment Deferral Plan. Effective March 23, 2020, any vehicles due for curtailment from March 16, 2020 through May 31, 2020 may be curtailed with the payment for principal, fees, and interest deferred until each customer's first contracted curtailment following May 31, 2020 (all deferred amounts will be due on such Curtailment Date).
The following guidelines are in place:
• Dealers will have the ability to make a payment against principal if they so choose
• Sold units are still required to be paid according to current policy
Units due for payment prior to March 16, 2020 or that are currently past due are not eligible
Any vehicles being requested for additional curtailments outside of contracted terms will require Regional Director approval
• No fees, including curtailment fees, are being waived
• Dealers will not be eligible if:
o they have sold out of trust (SOT) units;
o they have repo units; or
o if AFC has taken legal action against them

redpajama_set_name: RedPajamaCommonCrawl


text:
Q: define orderby URL with meta_key=post_views_count I want to make an url with meta_key=post_views_count.
E.g: http://www.mysampleweb.com/?meta_key=post_views_count&orderby=meta_value&order=DESC
So the posts should be ordered by Views.
Views are counting in the backend - but the url doesn't work. Just orders after newest/oldest posts.
How can I setup this query? Like it is above, it's not working.
Do I have to put something into functions.php - a rewerite Rule?
Thank you!
AD

redpajama_set_name: RedPajamaStackExchange


text:
Tragedy hits the sets of Amitabh,Chiranjeevi's next
Last week, a fire on the sets of Amitabh Bachchan and Chiranjeevi starrer Sye Raa Narasimha Reddy erupted, However, no one was hurt during it. But seems like another tragedy has struck the sets of the film. This week a 38-year-old Russian actor named Alexander died on set due to a heat stroke. He was playing an extra in the film. According to a report by a leading daily, Alexander's body was found near the DLF building on Tuesday and he was rushed to the hospital immediately.


Explore the limits. This dynamic practice is ideal for intermediate students and yoga teachers. The Vinyasa Level 2/3 practice is founded in precise alignment and the class sequences intelligently towards more intermediate postures, including inversions, backbends and arm balances. Often using props and the wall as support, we will build strength and refinement in the physical body, keeping us safe and structurally sound while allowing us to explore new depths of practice.
There are no upcoming "Vinyasa - Non-heated (L2/3)" classes on this day.
Really good and fun class!
Loved it! Thanks Dunc, you're the best!


text:
Q: Two key combos for Enclosing Folder, but same result While exploring the effect of different keys on menus in Finder in Lion, I spotted what seems like a redundancy in the case of the Go menu.
When pressing the ⌥ key while the menu is open, not only is the user Library added to the menu list, but the key combination for Enclosing Folder changes from ⌘↑ to ⌥⌘↑. The additional modifier appears to be redundant, since the same enclosing folder opens with both key combinations.

At first I thought this was Lion-specific, so was surprised to notice that this also applies in Snow Leopard.
Can others confirm that they too experience this behaviour and, if so, explain why the two combinations exist?

A: In Lion and Snow Leopard, the option/alt key makes a subtle change:

*

*⌘+▲ reuses the existing finder window (preserving sort, view and size of the child folder).

*⌥+⌘+▲ opens a new finder window with the saved settings for that parent folder (closing the child folder)


text:
Mystik U #2 Review
Oh, Oh, Oh It's Magik
Written by: Alisa Kwitney
Art by: Michael Norton and Jordie BellaireCover Price: $5.99
I really liked the first issue of Mystik U and while that wasn't a total surprise, it was a pleasant one still. This book had been delayed and canceled so often that I never thought it would see the light of day. Well, I'm glad it did because it scratched the Dark Universe itch that I didn't even know I wanted to scratch and so I'm sure those more inclined to like this sort of thing were jumping for joy. So, is this second issue just as good? Let's find out...
The issue opens with a prelude exchange between Rose and Richard where Rose agrees to let her other half look for the Malevolence as long as he promises not to kill any of the students. It's then off to a scene with Zatana trying to do magic for Mr. E, but even as he tells her the odds are skewed in her favor, she continues to fail miserably. It's then off to Dr. Occult's Introduction to Summoning Class and if you want to throw shade at this book for being DC's Harry Potter go ahead, that's one of the reasons I like it so much!
The lesson is all about summoning an Elemental and while it was cool to see each's students different methods, I suspect that Occult had more going on here than just a beginner's lesson. It pretty much ends up with more bad grades for Zatanna, but her luck turns quickly as she is invited to rush Triae Society...whatever that is. It certainly has something to do with bees!
It's like a typical college party...talking, dancing, a nectar of the gods that unlocks your potential. Pia continues being my favorite character as she acts like a lump on a log (like me!) while Zatanna gets chummy with Faust. It all goes downhill quickly and the gang leaves, but the Triae Society act all mysterious and evil and I don't trust them one damn bit! As the issue continues, Zatanna gets more and more into the Society and it's already causing a rift between her and Pia. It is, however, making her better at magic and also very sleepy.
We continue with a good moment with Pia and Plop (second favorite character!) about appearances, a little more background on Faust and then a real close up look at what Sebastian really is deep down inside. It leads to Zatanna being a jerk to Pia and really, this whole plotline of romance between Zatanna and Faust seemed hurried and was not set up well enough to have much of an impact.
The issue continues with the reader, and Zatanna, seeing what the Society was after all along, Faust getting a visit from his father and the whole gang showing up to save Z. Unfortunately, it looks they also summoned the Malevolence in the process. It all leads to a little downtime before a cliffhanger that can't be real but is certainly crazy!
I liked this issue, maybe a little less than the first, but there are some really good things going on here. While the story was a bit disjointed and felt like a couple of longer scenes minimized and crammed together, Alisa Kwitney does some really good character work and her dialogue is great. I just wish she had more time and issues to really flesh everything out. I loved the art is this issue. Again, it's all about the characters and Michael Norton does a great job with all of them.
I continue liking what Alisa Kwitney is giving us here even though this issue felt a bit more pieced together and rushed than the first. If you like good character work and art with a magical twist, this book is something you should check out.
Posted by Jim Werner
Labels: Comics, dc comics, mystik u, WSReview, Zatanna
Injustice 2 #43 Review
Weird Science DC Comics Podcast Spotlight: Detecti...
Dark Ark #5 Review
Kill The Minotaur TP Review
Black Lightning: Lawanda: The Book of Burial - Rev...
DC Comics Best Covers of the Week 1/31/18
Flash: Honey, I Shrunk Team Flash - Review and **S...
Motherlands #1 Review and ***SPOILERS***
Harley Quinn #36 Review and **SPOILERS**
Justice League of America/Doom Patrol #1 Review an...
Deathstroke Annual #1 Review
The Flash Annual #1 Review
The Silencer #1 Review
Detective Comics Annual #1 Review
Dark Nights Metal #5 Review
PREVIEW: The Flash Annual #1
PREVIEW: Detective Comics Annual #1
PREVIEW: Dark Nights Metal #5
PREVIEW: Deathstroke Annual #1
PREVIEW: Harley Quinn #36
PREVIEW: Justice League of America/Doom Patrol #1
Arrow: "We Fall" - Review and **SPOILERS**
Weird Science DC Comics Podcast Ep 160: DC Comics,...
Star Wars: Poe Dameron #23 Review
Marvel Two-In-One #2 Review - Marvel Monday
Rorschach #4 Review
Another One Writer: Tom King Artist: Jorge Fornes Colorist: Dave Stewart Letters: Clayton Cowles Cover Price: $4.99 Release Date: January 18...


Bardolph, get thee before to Coventry. Fill me a bottle of sack. Our soldiers shall march through. We'll to Sutton Coldfield tonight.
Bardolph, go ahead of us to Coventry, and fill me a bottle of wine. Our army will keep marching, and we'll make it to Sutton Coldfield tonight.
Will you give me some money, captain?
an angel = several shillings; Bardolph claims he has spent an angel on Falstaff's wine.
An if it do, take it for thy labor. An if it make twenty, take them all. I'll answer the coinage. Bid my lieutenant Peto meet me at town's end.
Well, if this bottle earns you an angel, then keep it for your troubles. If you earn twenty angels, then keep them all; I'm good for it. Tell my lieutenant Peto to meet me at the city limit.