text:
/*
*
* Routes
*
* */

// Module dependencies
var config = require('../config.js'),
    crypto = require('crypto'),
    net = require('net'),
    async = require('async'),
    hat = require('hat'),
    User = require('../models/user.js'),
    Admin = require('../models/admin.js'),
    Domain = require('../models/domain.js'),
    Record = require('../models/record.js'),
    check = require('validator').check,
    sanitize = require('validator').sanitize,
    tld = require('tldjs'),
    nodemailer = require("nodemailer");
    // dns = require('dns');

// create reusable transport method (opens pool of SMTP connections)
var smtpTransport = nodemailer.createTransport("SMTP", {
    service: config.serviceMailSMTP,
    // host: "smtp.moedns.com", // hostname
    //secureConnection: true, // use SSL
    // port: 465, // port for secure SMTP
    auth: {
        user: config.serviceMailUser,
        pass: config.serviceMailPass
    }
});

// function to test a object is empty.
// Via http://stackoverflow.com/questions/4994201/is-object-empty
var hasOwnProperty = Object.prototype.hasOwnProperty;
Object.prototype.isEmpty = function(obj) {
    // null and undefined are empty
    if (obj == null) return true;
    // Assume if it has a length property with a non-zero value
    // that that property is correct.
    if (obj.length && obj.length > 0)    return false;
    if (obj.length === 0)  return true;

    for (var key in obj) {
        if (hasOwnProperty.call(obj, key))    return false;
    }

    return true;
}

// Route functions
module.exports = function(app) {

    app.get('/', csrf, function(req, res) {
        res.render('index', {
            siteName: config.siteName,
            siteTagline: config.siteTagline,
            title: res.__('HOME') + ' - ' + config.siteName,
            allowReg: config.allowReg,
            user: req.session.user,
            success: req.flash('success').toString(),
            error: req.flash('error').toString()
        });
    });

    /* User routes */
    // Registration
    app.get('/reg', csrf, checkNotLogin, function(req, res) {
        if (!config.allowReg) {
            res.redirect('/');
            return req.flash(res.__("REG_NOT_ALLOWED"));
        }
        res.render('reg',{
            title: res.__('REGISTER') + ' - ' + config.siteName,
            siteName: config.siteName,
            siteTagline: config.siteTagline,
            allowReg: config.allowReg,
            user: req.session.user,
            allowReg: config.allowReg,
            success: req.flash('success').toString(),
            error: req.flash('error').toString()
        });
    });

    app.post('/reg', csrf, checkNotLogin, function(req,res){
        if (!config.allowReg) {
            res.redirect('/');
            return req.flash(res.__("REG_NOT_ALLOWED"));
        }
        var name = req.body.username,
            mail = req.body.email,
            password = req.body.password,
            repeatPassword = req.body['password-repeat'];

        try {
            check(name, 'USERNAME_EMPTY').notEmpty();
            check(name, 'USERNAME_ALPHANUMERIC').isAlphanumeric();
            check(password, 'PASSWORD_EMPTY').notEmpty();
            check(repeatPassword, 'PASSWORD_NOT_EQUAL').equals(password);
            check(mail, 'EMAIL_INVALID').len(4, 64).isEmail();
        } catch (e) {
            req.flash('error', res.__(e.message));
            return res.redirect('/reg');
        }

        // get password hash
        var hash = crypto.createHash('sha256'),
            password = hash.update(req.body.password).digest('hex');
        var newUser = new User({
            name: name,
            password: password,
            email: mail,
            activekey: hat(),
            role: 'inactive'
        });
        // check if username exists.
        User.check(newUser.name, newUser.email, function(err, user){
            // console.log(user);
            if(user) {
                err = 'USER_EXISTS';
            }
            if(err) {
                req.flash('error', res.__(err));
                return res.redirect('/reg');
            }
            newUser.save(function(err){
                if(err){
                    req.flash('error',err);
                    return res.redirect('/reg');
                }
                // Send verification Email.
                var activeLink = 'http://' + config.url + '/activate?activekey=' + newUser.activekey;
                if (config.ssl) {
                    activeLink = 'https://' + config.url + '/activate?activekey=' + newUser.activekey;
                }
                // console.log(activeLink);
                var mailOptions = {
                    from: config.serviceMailSender, // sender address
                    to: newUser.email, // list of receivers
                    subject: res.__('USER_VERIFICATION') + ' - ' + config.siteName, // Subject line
                    text: res.__('USER_VERIFICATION_BODY', newUser.name, config.siteName, activeLink)
                }
                // console.log(mailOptions.text);
                // send mail with defined transport object
                smtpTransport.sendMail(mailOptions, function(err, response) {
                    // console.log('executed');
                    if (err) {
                        console.log(err);
                    }
                    smtpTransport.close();
                    // req.session.user = newUser; // store user information to session.
                    req.flash('success',res.__('REG_AWAITING_VERIFICATION'));
                    res.redirect('/');
                });

            });
        });
    });

    // User activation
    app.get('/activate', checkNotLogin, function(req, res) {
        var activekey = req.query.activekey;
        User.checkActivekey(activekey, function(err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/');
            }

            if (!user) {
                req.flash('error', res.__('USER_ACTIVATED_NOT_EXIST'));
                return res.redirect('/');
            }

            User.activate(activekey, function(err) {
                if (err) {
                    req.flash('error', err);
                    return res.redirect('/');
                }
                req.session.user = user;
                req.flash('success', res.__('USER_ACTIVATED'));
                res.redirect('/');
            });
        });
    });

    // Login/logout
    app.get('/login', csrf, checkNotLogin, function(req,res){
        res.render('login',{
            title: res.__('LOGIN') + ' - ' + config.siteName,
            siteName: config.siteName,
            siteTagline: config.siteTagline,
            allowReg: config.allowReg,
            user: req.session.user,
            success: req.flash('success').toString(),
            error: req.flash('error').toString()
        });
    });

    app.post('/login', csrf, checkNotLogin, function(req, res){
        // Generate password hash
        var hash = crypto.createHash('sha256'),
            password = hash.update(req.body.password).digest('hex');
        // check login details
        try {
            check(req.body.username, 'USERNAME_ALPHANUMERIC').isAlphanumeric();
        } catch (e) {
            req.flash('error', res.__(e.message));
            return res.redirect('/login');
        }
        User.get(req.body.username, function(err, user) {
            if (!user) {
                req.flash('error', res.__('LOGIN_FAIL'));
                return res.redirect('/login');
            } else if (user.password != password) {
                // Send warning message.
                var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
                // setup e-mail data with unicode symbols
                var mailOptions = {
                    from: config.serviceMailSender, // sender address
                    to: user.email, // list of receivers
                    subject: res.__('LOGIN_FAIL_WARNING') + ' - ' + config.siteName, // Subject line
                    text: res.__('LOGIN_FAIL_WARNING_BODY') + ip // plaintext body
                }
                // send mail with defined transport object
                smtpTransport.sendMail(mailOptions, function(err, response) {
                    // console.log('executed');
                    if (err) {
                        console.log(err);
                    }
                    smtpTransport.close();
                    req.flash('error', res.__('LOGIN_FAIL'));
                    return res.redirect('/login');
                });
            } else {
                if (user.role == 'inactive') {
                    req.flash('error', res.__('USER_NOT_ACTIVATED'));
                    return res.redirect('/login');
                } else {
                    req.session.user = user;
                    req.flash('success', res.__('LOGIN_SUCCESS'));
                    res.redirect('/');
                }
            }
        });
    });

    // Password recovery
    app.get('/forgot-password', csrf, checkNotLogin, function(req, res) {
        res.render('forgot-password',{
            title: res.__('RESET_PASSWORD') + ' - ' + config.siteName,
            siteName: config.siteName,
            siteTagline: config.siteTagline,
            allowReg: config.allowReg,
            user: req.session.user,
            success: req.flash('success').toString(),
            error: req.flash('error').toString()
        });
    });

    app.post('/forgot-password', csrf, checkNotLogin, function(req, res) {
        var mail = req.body.email,
            ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

        // Check email format
        try {
            check(mail, 'EMAIL_INVALID').len(4, 64).isEmail();
        } catch (e) {
            req.flash('error', res.__(e.message));
            return res.redirect('/forgot-password');
        }

        User.check(null, mail, function(err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/forgot-password');
            }
            if (!user) {
                req.flash('error', res.__('USER_NOT_FOUND'));
                return res.redirect('/forgot-password');
            }

            // Get user info, generate key then send to user.
            User.createResetkey(user.name, user.email, function(err, resetkey) {
                if (err) {
                    req.flash('error', err);
                    return res.redirect('/forgot-password');
                }
                // console.log(resetkey);

                var resetLink = 'http://' + config.url + '/reset-password?resetkey=' + resetkey;
                if (config.ssl) {
                    resetLink = 'https://' + config.url + '/reset-password?resetkey=' + resetkey;
                }
                // console.log(resetLink);
                var mailOptions = {
                    from: config.serviceMailSender, // sender address
                    to: user.email, // list of receivers
                    subject: res.__('RESET_PASSWORD') + ' - ' + config.siteName, // Subject line
                    text: res.__('RESET_PASS_BODY_1') + resetLink + res.__('RESET_PASS_BODY_2') + ip // plaintext body
                }
                // send mail with defined transport object
                // console.log(mailOptions.text);
                smtpTransport.sendMail(mailOptions, function(err, response) {
                    // console.log(response);
                    if (err) {
                        console.log(err);
                    }
                    // User.clearResetkey(resetkey);
                    smtpTransport.close();
                    req.flash('success', res.__('RESET_MAIL_SENT'));
                    return res.redirect('/login');
                });
            });
        });

    });

    app.get('/reset-password', csrf, checkNotLogin, function(req, res) {
        User.checkResetkey(req.query.resetkey, function(err, doc) {
            if (err) {
                console.log(err);
            }
            if (!doc) {
                req.flash(res.__('USER_NOT_FOUND'));
                res.redirect('/');
            } else {
                res.render('reset-password',{
                    title: res.__('RESET_PASSWORD') + ' - ' + config.siteName,
                    siteName: config.siteName,
                    siteTagline: config.siteTagline,
                    allowReg: config.allowReg,
                    user: req.session.user,
                    success: req.flash('success').toString(),
                    error: req.flash('error').toString()
                });
            }
        });
    });

    app.post('/reset-password', csrf, checkNotLogin, function(req, res) {
        var resetkey = req.query.resetkey;

        try {
            check(req.body.password, 'PASSWORD_EMPTY').notEmpty();
            check(req.body['password-repeat'], 'PASSWORD_NOT_EQUAL').equals(req.body.password);
            check(resetkey, 'RESETKEY_INCORRECT').isAlphanumeric().len(32);
        } catch (e) {
            req.flash('error', res.__(e.message));
            return res.redirect('/');
        }

        // get password hash
        var hash = crypto.createHash('sha256'),
            password = hash.update(req.body.password).digest('hex');

        User.checkResetkey(resetkey, function(err, user) {
            if (err) {
                req.flash('error', res.__(err));
                return res.redirect('/');
            }
            if (!user) {
                req.flash('error', res.__('USER_NOT_FOUND'));
                return res.redirect('/');
            }

            var newUser = new User({
                name: user.name,
                password: password,
                email: user.email
            });

            User.edit(newUser, function(err, user){
                if(err) {
                    req.flash('error', res.__(err));
                    return res.redirect('/');
                }
                req.flash('success', res.__('PASSWORD_UPDATED'));
                req.session.user = null;
                res.redirect('/login');
            });

        });

    });

    app.post('/logout', csrf, checkLogin, function(req, res) {
        req.session.user = null;
        req.flash('success',res.__('LOGOUT_SUCCESS'));
        res.redirect('/');
    });

    app.get('/account', csrf, checkLogin, function(req, res) {
        res.render('account',{
            title: res.__('MY_ACCOUNT') + ' - ' + config.siteName,
            siteName: config.siteName,
            siteTagline: config.siteTagline,
            allowReg: config.allowReg,
            user: req.session.user,
            success: req.flash('success').toString(),
            error: req.flash('error').toString()
        });
    });

    app.post('/account', csrf, checkLogin, function(req, res) {
        var email = req.body.email,
            hash = crypto.createHash('sha256'),
            password = hash.update(req.body.password).digest('hex'),
            newPassword = req.body.newpass,
            repeatPassword = req.body['password-repeat'],
            inputError = '';

        if (password != req.session.user.password) {
            inputError = 'WRONG_PASSWORD';
        }
        if (repeatPassword || newPassword) {
            var hash = crypto.createHash('sha256'),
                newPassword = hash.update(newPassword).digest('hex');
            var hash = crypto.createHash('sha256'),
                repeatPassword = hash.update(repeatPassword).digest('hex');
            if (repeatPassword != newPassword) {
                inputError = 'PASSWORD_NOT_EQUAL';
            }
            password = newPassword;
        }

        try {
            check(email, 'EMAIL_INVALID').len(4, 64).isEmail();
        } catch (e) {
            inputError = e.message;
        }

        if (inputError) {
            req.flash('error', res.__(inputError));
            return res.redirect('/account');
        }

        var newUser = new User({
            name: req.session.user.name,
            email: email,
            password: password
        });

        User.check(null, newUser.email, function(err, user){
            // console.log(user);
            if(user && newUser.email != req.session.user.email) {
                err = 'USER_EXISTS';
            }
            if(err) {
                req.flash('error', res.__(err));
                return res.redirect('/account');
            }

            User.edit(newUser, function(err, user){
	        console.log(err);
                if(err) {
                    req.flash('error', res.__(err));
                    return res.redirect('/account');
                }
                req.flash('success', res.__('USER_UPDATED'));
                req.session.user = null;
                res.redirect('/login');
            });
        });
    });

    /* Domain routes */
    // List domains under a user
    app.get('/domains', csrf, checkLogin, function(req, res) {
       Domain.getList(req.session.user.name, function(err, domains) {
           if(err){
               req.flash('error',err);
               return res.redirect('/domains');
           }
           res.render('domains', {
               title: res.__('MY_DOMAINS') + ' - ' + config.siteName,
               siteName: config.siteName,
               siteTagline: config.siteTagline,
               allowReg: config.allowReg,
               user: req.session.user,
               domains: domains,
               powerservers: config.powerservers,
               success: req.flash('success').toString(),
               error: req.flash('error').toString()
           });
       })
    });

    // Add domain
    app.post('/add-domain', csrf, checkLogin, function(req, res) {
        // Validate whether user input is root domain name.
        var newDomain = new Domain({
            name: req.body.domain,
            belongs: req.session.user.name
        });
        if (tld.getDomain(newDomain.name) == newDomain.name &&
            tld.tldExists(newDomain.name) &&
            /^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$/.test(newDomain.name)) {
            // Domain valid, check if domain exists in db.
            Domain.check(newDomain.name, function(err, data) {
                if (data) {
                    // console.log(data);
                    // Domain exists, return error.

                    req.flash('error', res.__('DOMAIN_EXISTS'));
                    return res.redirect('/domains');
                } else {
                    // Domain not exist, insert into database.
                    newDomain.save(function(err) {
                        if (err) {
                            res.redirect('/domains');
                            return req.flash('error',err);
                        }
                        req.flash('success',res.__('ADD_DOMAIN_SUCCESS'));
                        res.redirect('/domains');
                    });
                }
            });
        } else {
            req.flash('error', res.__('DOMAIN_NOT_VALID'));
            return res.redirect('/domains');
        }
    });

    // Remove a domain
    app.post('/domain/:domain/delete', csrf, checkLogin, function(req, res) {
        var domain = req.params.domain,
            id = parseInt(req.body.domainId),
            user = req.session.user;
        // console.log(id);
        // console.log(domain);
        // console.log(user.name);
        Domain.checkOwner(domain, user.name, function(err, doc) {
            // console.log(doc);
            if (err) {
                req.flash('error', err);
                return res.redirect('/domains');
            }
            if (doc == null) {
                req.flash('error', res.__('DOMAIN_NOT_OWNED'));
                return res.redirect('/domains');
            } else {
                Domain.remove(id, user.name, function(err) {
                    if (err) {
                        req.flash('error', err);
                        return res.redirect('/domains');
                    }
                    req.flash('success', res.__('DOMAIN_DELETED'));
                    res.redirect('/domains');
                });
            }
        });

    });

    app.get('/domain/:domain', csrf, checkLogin, function(req, res) {
       var domain = req.params.domain,
           user = req.session.user;
       Domain.checkOwner(domain, user.name, function(err, doc) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/domains');
            }
            if (doc == null) {
                req.flash('error', res.__('DOMAIN_NOT_OWNED'));
                return res.redirect('/domains');
            } else {
                // Get domain records
                // console.log(doc);
                Record.getList(doc.id, function(err, records) {
                    if (err) {
                        req.flash('error', err);
                        return res.redirect('/domains');
                    }
                    // console.log(records);
                    res.render('records', {
                        title: res.__('DOMAIN') + ': ' + domain.toUpperCase() + ' - ' + config.siteName,
                        siteName: config.siteName,
                        siteTagline: config.siteTagline,
                        allowReg: config.allowReg,
                        user: req.session.user,
                        domain: doc,
                        powerservers: config.powerservers,
                        records: records,
                        success: req.flash('success').toString(),
                        error: req.flash('error').toString()
                    });
                });
            }
       });

    });

    /*
    * Record routes
    * */
    // Add a record.
    app.post('/domain/:domain/add-record', csrf, checkLogin, function(req, res) {
        // console.log(req.body);
        var type = req.body.type,
            // name = req.body.name == '@'?req.params.domain:req.body.name + '.' + req.params.domain,
            ttl = req.body.ttl || 3600,
            prio = req.body.prio || null,
            content = req.body.content,
            geo = req.body.geo || null;

        var name = null;

        // parse for root domain and the last '.'
        if (req.body.name == '@' || req.body.name == '') {
            name = req.params.domain;
        } else {
            name = req.body.name + '.' + req.params.domain;
        }

        if (content.indexOf('.', content.length - 1) != -1) {
            content = content.substr(0, content.length - 1);
        }

        var geoloc = null,
            geoisp = null;
        if (geo) {
            if (geo.search('/') != -1) {
                geoloc = geo.slice(0, geo.indexOf('/'));
                geoisp = geo.slice(geo.indexOf('/') + 1);
            } else if (geo.search('IP:') != -1) {
                var geoip = geo.slice(geo.lastIndexOf(':') + 1);
                geoloc = 'IP';
                geoisp = geoip.slice(0, geoip.lastIndexOf('.')) + '.0';
            } else {
                geoloc = geo;
                geoisp = null;
            }
        }

        try {
            check(ttl, 'TTL_ERROR').isDecimal().min(60);
            // if (geo) {
            //    check(geoloc, 'GEO_ERROR').isAlpha().len(2, 20);
            // }
            switch (type) {
                case "A":
                    check(content, 'NEED_IPV4').isIPv4();
                    prio = null;
                    break;
                case "AAAA":
                    check(content, 'NEED_IPV6').isIPv6();
                    prio = null;
                    break;
                case ("CNAME"):
                    if (tld.isValid(content) && tld.tldExists(content)) {
                        prio = null;
                    } else {
                        throw new Error("VALUE_ERROR");
                    }
                    break;
                case "NS":
                    if (tld.isValid(content) && tld.tldExists(content)) {
                        prio = null;
                    } else {
                        throw new Error("VALUE_ERROR");
                    }
                    break;
                case "MX":
                    if (!prio) {
                        prio = 10;
                    }
                    if (tld.isValid(content) && tld.tldExists(content)) {
                        //  Better DNS check module needed.
                        //    dns.resolve(content, function(err, addresses) {
                        //        console.log(addresses);
                        //        if (addresses === undefined) {
                        //            throw new Error("NEED_A_RECORD");
                        //        } else {
                        //
                        //        }
                        //    });
                        //
                    } else {
                        throw new Error("VALUE_ERROR");
                    }
                    check(prio, 'PRIO_ERROR').isDecimal().max(100).min(1);
                    break;
                case "SRV":
                    // _service._proto.name. TTL class SRV priority weight port target.
                    if (!prio) {
                        prio = 5;
                    }
                    if (req.body.weight == null) {
                        req.body.weight = 0;
                    }
                    if (req.body.port == null) {
                        req.body.port = 5222;
                    }
                    name = "_" + req.body.service + "._" + req.body.protocol + "." + req.params.domain;
                    content = req.body.weight + " " + req.body.port + " " + req.body.content;
                    break;
                case "TXT":
                    prio = null;
                    break;
                default:
                    throw new Error("TYPE_ERROR");
            }
        } catch (e) {
            // console.log(e);
            req.flash('error', res.__(e.message));
            return res.redirect('/domain/' + req.params.domain);
        }

        // TODO Check user inputs for record validity
        // Better RegEx required.
/*        try {
            check(type, 'TYPE_ERROR').isIn([
                "A",
                "AAAA",
                "CNAME",
                "NS",
                "MX",
                "SRV",
                "TXT"
            ]);
            check(ttl, 'TTL_ERROR').isDecimal().min(60);
            switch (type) {
                case "A":
                    check(content, 'NEED_IPV4').isIPv4();
                    prio = null;
                    break;
                case "AAAA":
                    check(content, 'NEED_IPV6').isIPv6();
                    prio = null;
                    break;
                case ("CNAME" || "NS"):
                    if (tld.isValid(content) && tld.tldExists(content)) {
                        prio = null;
                    } else {
                        throw new Error("VALUE_ERROR");
                    }
                    break;
                case "MX":
                    if (tld.isValid(content) && tld.tldExists(content)) {
                    //  Better DNS check module needed.
                    //    dns.resolve(content, function(err, addresses) {
                    //        console.log(addresses);
                    //        if (addresses === undefined) {
                    //            throw new Error("NEED_A_RECORD");
                    //        } else {
                    //
                    //        }
                    //    });
                    //
                    } else {
                        throw new Error("VALUE_ERROR");
                    }
                    check(prio, 'PRIO_ERROR').isDecimal().max(100).min(1);
                    break;
                case "SRV":
                    prio = null;
                    break;
                case "TXT":
                    prio = null;
                    break;
                default:
                    throw new Error("TYPE_ERROR");
            }
        } catch (e) {
            console.log(e);
            req.flash('error', res.__(e.message));
            return res.redirect('/domain/' + req.params.domain);
        }
*/

        Domain.checkOwner(req.params.domain, req.session.user.name, function(err, doc) {
            // console.log(doc);
            if (err) {
                // console.log(err);
                // req.flash('error', err);
                return res.redirect('/domain/' + req.params.domain);
            }
            if (doc == '') {
                // console.log(err);
                req.flash('error', res.__('DOMAIN_NOT_OWNED'));
                return res.redirect('/domains');
            } else {
                // console.log(content);
                var newRecord = new Record({
                    domainId: doc.id,
                    name: name,
                    type: type,
                    content: content,
                    ttl: ttl,
                    prio: prio,
                    geoloc: geoloc,
                    geoisp: geoisp
                });
                // console.log(newRecord);

                // Check for duplicate record
                Record.check(newRecord, function(err, result) {
                    // console.log(err);
                    // console.log(result)
                    // console.log(JSON.stringify(result));
                    if (err) {
                        req.flash("error", err);
                        return res.redirect('/domain/' + req.params.domain);
                    }
                    if (isEmpty(result)) {
                        // Add new record to db.
                        newRecord.save(function(saveResult) {
                            // console.log(err);
                            // console.log(saveResult);
                            // if (err) {
                            //    // console.log(err);
                            //    req.flash('error', err);
                            //    return res.redirect('/domain/' + req.params.domain);
                            // }
                            req.flash('success', res.__('ADD_RECORD_SUCCESS'));
                            res.redirect('/domain/' + req.params.domain);
                        });
                    } else {
                        req.flash('error', res.__('DUPLICATE_RECORD'));
                        return res.redirect('/domain/' + req.params.domain);
                    }
                });
            }
        });
    });

    app.get('/addrecordapi', checkLogin, function(req, res) {
        res.render('record-type');
    });

    // Remove a record
    app.post('/domain/:domain/delete-record', csrf, checkLogin, function(req, res) {
        var domain = req.params.domain,
            record = parseInt(req.body.recordId),
            user = req.session.user;
        Domain.checkOwner(domain, user.name, function(err, doc) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/domains');
            }
            if (doc == null) {
                req.flash('error', res.__('DOMAIN_NOT_OWNED'));
                return res.redirect('/domains');
            } else {
                Record.delete(record, function(err) {
                    if (err) {
                        req.flash('error', err);
                        return res.redirect('/domain/' + domain);
                    }
                    req.flash('success', res.__('RECORD_DELETED'));
                    res.redirect('/domain/' + domain);
                });
            }
        });
    });

    // Edit a record
    app.post('/domain/:domain/edit-record', csrf, checkLogin, function(req, res) {
        var domain = req.params.domain,
            user = req.session.user;
        Domain.checkOwner(domain, user.name, function(err, doc) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/domains');
            }
            if (doc == null) {
                req.flash('error', res.__('DOMAIN_NOT_OWNED'));
                return res.redirect('/domains');
            } else {
                // console.log(doc);
                // Validate user input and update record.
                var id = parseInt(req.body.recordId),
                    domainId = parseInt(doc.id),
                    type = req.body.type,
                    name = req.body.name == '@'?req.params.domain:req.body.name + '.' + req.params.domain,
                    ttl = req.body.ttl,
                    prio = req.body.prio,
                    geo = req.body.geo||null,
                    content = req.body.content;

                var geoloc = null,
                    geoisp = null;
                if (geo) {
                    if (geo.search('/') != -1) {
                        geoloc = geo.slice(0, geo.indexOf('/'));
                        geoisp = geo.slice(geo.indexOf('/') + 1);
                    } else if (geo.search('IP:') != -1) {
                        var geoip = geo.slice(geo.lastIndexOf(':') + 1);
                        geoloc = 'IP';
                        geoisp = geoip.slice(0, geoip.lastIndexOf('.')) + '.0';
                    } else {
                        geoloc = geo;
                        geoisp = null;
                    }
                }

                // TODO Check user inputs for record validity
                // Better RegEx required.
                try {
                    check(type, 'TYPE_ERROR').isIn([
                        "A",
                        "AAAA",
                        "CNAME",
                        "NS",
                        "MX",
                        "SRV",
                        "TXT"
                    ]);
                    check(ttl, 'TTL_ERROR').isDecimal().min(60);
                    // if (geo) {
                    //    check(geoloc, 'GEO_ERROR').isAlpha().len(2, 20);
                    // }
                    switch (type) {
                        case "A":
                            check(content, 'NEED_IPV4').isIPv4();
                            prio = null;
                            break;
                        case "AAAA":
                            check(content, 'NEED_IPV6').isIPv6();
                            prio = null;
                            break;
                        case "CNAME":
                            if (tld.isValid(content) && tld.tldExists(content)) {
                                prio = null;
                            } else {
                                throw new Error("VALUE_ERROR");
                            }
                            break;
                        case "NS":
                            if (tld.isValid(content) && tld.tldExists(content)) {
                                prio = null;
                            } else {
                                throw new Error("VALUE_ERROR");
                            }
                            break;
                        case "MX":
                            if (!prio) {
                                prio = 10;
                            }
                            if (tld.isValid(content) && tld.tldExists(content)) {
                                /*  Better DNS check module needed.
                                 dns.resolve(content, function(err, addresses) {
                                 console.log(addresses);
                                 if (addresses === undefined) {
                                 throw new Error("NEED_A_RECORD");
                                 } else {

                                 }
                                 });
                                 */
                            } else {
                                throw new Error("VALUE_ERROR");
                            }
                            check(prio, 'PRIO_ERROR').isDecimal().max(100).min(1);
                            break;
                        case "SRV":
                            break;
                        case "TXT":
                            prio = null;
                            break;
                        default:
                            throw new Error("TYPE_ERROR");
                    }
                } catch (e) {
                    console.log(e);
                    req.flash('error', res.__(e.message));
                    return res.redirect('/domain/' + req.params.domain);
                }
                var newRecord = new Record({
                    id: id,
                    domainId: domainId,
                    name: name,
                    type: type,
                    content: content,
                    ttl: ttl,
                    prio: prio,
                    geoloc: geoloc,
                    geoisp: geoisp
                });
                Record.edit(newRecord, function(err) {
                    if (err) {
                        req.flash('error', err);
                        return res.redirect('/domain/' + domain);
                    }
                    req.flash('success', res.__('RECORD_UPDATED'));
                    res.redirect('/domain/' + domain);
                });
            }
        });
    });

    app.post('/domain/:domain/pause-record', checkLogin, csrf, function(req, res) {
        var domain = req.params.domain,
            record = parseInt(req.body.recordId),
            user = req.session.user;
        Domain.checkOwner(domain, user.name, function(err, doc) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/domains');
            }
            if (doc == null) {
                req.flash('error', res.__('DOMAIN_NOT_OWNED'));
                return res.redirect('/domains');
            } else {
                Record.pauseRecord(record, function(err, result) {
                    if (err) {
                        req.flash('error', err);
                        return res.redirect('/domain/' + domain);
                    }
                    req.flash('success', res.__('RECORD_PAUSED'));
                    res.redirect('/domain/' + domain);
                });
            }
        });
    });

    app.post('/domain/:domain/resume-record', checkLogin, csrf, function(req, res) {
        var domain = req.params.domain,
            record = parseInt(req.body.recordId),
            user = req.session.user;
        Domain.checkOwner(domain, user.name, function(err, doc) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/domains');
            }
            if (doc == null) {
                req.flash('error', res.__('DOMAIN_NOT_OWNED'));
                return res.redirect('/domains');
            } else {
                Record.resumeRecord(record, function(err, result) {
                    if (err) {
                        req.flash('error', err);
                        return res.redirect('/domain/' + domain);
                    }
                    req.flash('success', res.__('RECORD_RESUMED'));
                    res.redirect('/domain/' + domain);
                });
            }
        });
    });

    // Server status
    app.get('/status', csrf, checkLogin, function(req, res) {
        var statusservers = config.powerservers;
        if (config.statusservers) {
            statusservers = config.statusservers;
        }
        res.render('status', {
            title: res.__('SERVER_STATUS') + ' - ' + config.siteName,
            siteName: config.siteName,
            siteTagline: config.siteTagline,
            allowReg: config.allowReg,
            user: req.session.user,
            powerservers: statusservers,
            success: req.flash('success').toString(),
            error: req.flash('error').toString()
        });
    });

    app.get('/statusapi/:server', checkLogin, function(req, res) {
        console.log(req.session.user.name + ' requested status for ' + req.params.server);
        var server = req.params.server,
            status = null,
            sock = new net.Socket();
        sock.setTimeout(3000);
        sock.on('connect', function() {
            console.log(server + ' is up.');
            res.send("0");
            sock.destroy();
        }).on('error', function(e) {
            console.log(server + ' is down: ' + e.message);
            res.send("1");
        }).on('timeout', function(e) {
            console.log(server + ' is down: timeout');
            res.send("2");
        }).connect(53, server);
    });

    /* About page */
    app.get('/about', csrf, function(req, res) {
        res.render('about', {
            title: res.__('ABOUT') + ' - ' + config.siteName,
            siteName: config.siteName,
            siteTagline: config.siteTagline,
            allowReg: config.allowReg,
            user: req.session.user,
            success: req.flash('success').toString(),
            error: req.flash('error').toString()
        });
    });

    /* Help page */
    app.get('/help', csrf, function(req, res) {
        res.render('help', {
            title: res.__('HELP') + ' - ' + config.siteName,
            siteName: config.siteName,
            siteTagline: config.siteTagline,
            allowReg: config.allowReg,
            user: req.session.user,
            success: req.flash('success').toString(),
            error: req.flash('error').toString()
        });
    });

    /*
    * Contact page
    * */
    app.get('/contact', csrf, function(req, res) {
        res.render('contact', {
            title: res.__('CONTACT') + ' - ' + config.siteName,
            siteName: config.siteName,
            siteTagline: config.siteTagline,
            allowReg: config.allowReg,
            user: req.session.user,
            success: req.flash('success').toString(),
            error: req.flash('error').toString()
        });
    });

    app.post('/contact', csrf, function(req, res) {
        // Get user IP address.
        var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;

        var reply = req.body.email || req.session.user.email,
            subject = req.body.subject + " - " + config.siteName;

        var body = res.__('SENDER_MAIL') + reply + '\n\n' + req.body.message + '\n\n' + res.__('IP_ADDR')  + ip;

        // console.log(ip);
        // console.log(from);
        // console.log(to);
        // console.log(subject);
        // console.log(body);
        if (!req.body.subject || !req.body.message) {
            req.flash('error', res.__('MISSING_FIELD'));
            return res.redirect('/contact');
        }

        try {
            check(reply, 'EMAIL_INVALID').isEmail();
        } catch (e) {
            req.flash('error', res.__(e.message));
            return res.redirect('/contact');
        }
        // console.log(reply);

        // setup e-mail data with unicode symbols
        var mailOptions = {
            from: config.serviceMailSender, // sender address
            to: config.adminMail, // list of receivers
            replyTo: reply,
            subject: subject, // Subject line
            text: body // plaintext body
        }

        // console.log('executed');
        // send mail with defined transport object
        smtpTransport.sendMail(mailOptions, function(err, response) {
            // console.log('executed');
            if (err) {
                console.log(err);
                req.flash('error', err);
                return res.redirect('/contact');
            } else {
                req.flash('success', res.__('MSG_SENT'))
                res.redirect('/');
            }

            // if you don't want to use this transport object anymore, uncomment following line
            smtpTransport.close(); // shut down the connection pool, no more messages
        });
    });

    /*
     * APIs
     * */
    app.post('/getapi', csrf, checkLogin, function(req, res) {
        User.createApi(req.session.user.name, function(err, apikey) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/api');
            }
            req.flash('success', res.__('API_GET'));
            res.redirect('/myapi');
        });
    });

    app.get('/myapi', csrf, checkLogin, function(req, res) {
        User.getApi(req.session.user.name, function(err, apikey) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/myapi');
            }
            res.render('api', {
                title: res.__('API_ACCESS') + ' - ' + config.siteName,
                siteName: config.siteName,
                siteTagline: config.siteTagline,
                allowReg: config.allowReg,
                user: req.session.user,
                apikey: apikey,
                success: req.flash('success').toString(),
                error: req.flash('error').toString()
            });
        });
    });

    // DDNS
    app.get('/api', function(req, res) {
        // console.log(req.query);
        var domain = req.query.domain,
            recordId = req.query.id,
            type = req.query.type.toUpperCase(),
            ip = req.query.ip,
            ttl = parseInt(req.query.ttl) || 60,
            nat = req.query.nat,
            apikey = req.query.key + '';
        if (nat) {
            ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
        } else {
            ip = req.query.ip;
        }
        if (!apikey) {
            return res.send(401, 'User unauthorized.');
        }
        User.checkApi(apikey, function(err, user) {
            if (err) {
                return res.send(500, 'Server error.');
            }
            if (!user) {
                return res.send(401, 'User unauthorized.');
            } else {
                // console.log(tld.getDomain(domain));
                // console.log(user.name);
                Domain.checkOwner(tld.getDomain(domain), user.name, function(err, doc) {
                    if (err) {
                        return res.send(500, 'Server error.');
                    }
                    if (doc == null) {
                        return res.send(401, 'Domain unauthorized.');
                    } else {
                        // Validate user input and update record.
                        var domainId = parseInt(doc.id),
                            name = domain,
                            prio = null,
                            content = ip;
                        // TODO Check user inputs for record validity
                        // Better RegEx required.
                        try {
                            check(type, 'TYPE_ERROR').isIn([
                                "A",
                                "AAAA"
                            ]);
                            switch (type) {
                                case "A":
                                    check(content, 'NEED_IPV4').isIPv4();
                                    break;
                                case "AAAA":
                                    check(content, 'NEED_IPV6').isIPv6();
                                    break;
                            }
                        } catch (e) {
                            // console.log(e);
                            return res.send(400, 'Bad request.');
                        }
                        var newRecord = new Record({
                            id: recordId,
                            domainId: domainId,
                            name: name,
                            type: type,
                            content: ip,
                            ttl: ttl,
                            prio: prio
                        });
                        Record.edit(newRecord, function(err, result) {
			    // console.log(result);
                            if (err) {
                                return res.send(500, 'Server error.');
			    } else if (!result) {
				return res.send(400, 'Bad request.');
                            } else if (result.changedRows === 0) {
                                return res.send(200, 'Record unchange.');
			    } else {
                                return res.send(200, 'Record updated.');
                            }
                        });
                    }
                });
            }
        });
    });

    /*
    * Admin routes
    * */

    // Admin dashboard
    app.get('/admin', csrf, checkLogin, function(req, res) {
        // console.log(req.session.user);
        User.check(req.session.user.name, req.session.user.email, function(err, user) {
            // console.log(user);
            if (err) {
                req.flash('error', err);
                return res.redirect('/');
            }
            if(user.role != 'admin') {
                req.flash('error', res.__('NO_PERMISSION'));
                return res.redirect('/');
            } else {
                Admin.stats(function(err, stats) {
                    if (err) {
                        req.flash('error', err);
                    }
                    res.render('admin', {
                        siteName: config.siteName,
                        siteTagline: config.siteTagline,
                        title: res.__('ADMIN_INDEX') + ' - ' + config.siteName,
                        allowReg: config.allowReg,
                        user: req.session.user,
                        stats: stats,
                        success: req.flash('success').toString(),
                        error: req.flash('error').toString()
                    });
                });
            }
        });
    });

     // Get users list
    app.get('/admin/userlist', csrf, checkLogin, function(req, res) {
        var page = req.query.p?parseInt(req.query.p):1,
            limit = req.query.limit?parseInt(req.query.limit):50;
        // Check user has permission to access admin dashbord.
        User.check(req.session.user.name, req.session.user.email, function(err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/');
            }
            if(user.role != 'admin') {
                req.flash('error', res.__('NO_PERMISSION'));
                return res.redirect('/');
            } else {
                Admin.userlist(page, limit, function(err, users) {
                    if(err) {
                        users = [];
                        req.flash('error', err);
                    }
                    // console.log(users.length);
                    res.render('userlist', {
                        siteName: config.siteName,
                        siteTagline: config.siteTagline,
                        title: res.__('USERS_LIST') + ' - ' + config.siteName,
                        allowReg: config.allowReg,
                        user: req.session.user,
                        users: users,
                        paginationData: users,
                        paginationLimit: limit,
                        page: page,
                        success: req.flash('success').toString(),
                        error: req.flash('error').toString()
                    });
                });
            }
        });
    });

    app.post('/admin/adduser', csrf, checkLogin, function(req, res) {
        User.check(req.session.user.name, req.session.user.email, function(err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/');
            }
            if(user.role != 'admin') {
                req.flash('error', res.__('NO_PERMISSION'));
                return res.redirect('/');
            } else {
                var name = req.body.username,
                    mail = req.body.email,
                    password = req.body.password,
                    repeatPassword = req.body['password-repeat'],
                    role = req.body.role;

                try {
                    check(name, 'USERNAME_EMPTY').notEmpty();
                    check(name, 'USERNAME_ALPHANUMERIC').isAlphanumeric();
                    check(password, 'PASSWORD_EMPTY').notEmpty();
                    check(repeatPassword, 'PASSWORD_NOT_EQUAL').equals(password);
                    check(mail, 'EMAIL_INVALID').len(4, 64).isEmail();
                } catch (e) {
                    req.flash('error', res.__(e.message));
                    return res.redirect('/admin/userlist');
                }

                // get password hash
                var hash = crypto.createHash('sha256'),
                    password = hash.update(req.body.password).digest('hex');
                var newUser = new User({
                    name: name,
                    password: password,
                    email: mail,
                    role: role
                });
                // check if username exists.
                User.check(newUser.name, newUser.email, function(err, user){
                    // console.log(user);
                    if(user) {
                        err = 'USER_EXISTS';
                    }
                    if(err) {
                        req.flash('error', res.__(err));
                        return res.redirect('/admin/userlist');
                    }
                    newUser.save(function(err){
                        if(err){
                            req.flash('error',err);
                            return res.redirect('/reg');
                        }
                        req.flash('success',res.__('ADD_USER_SUCCESS'));
                        res.redirect('/admin/userlist');
                    });
                });
            }
        });
    });

    app.post('/admin/deleteuser', csrf, checkLogin, function(req, res) {
        // console.log(req.body.username);
        User.check(req.session.user.name, req.session.user.email, function(err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/');
            }
            if(user.role != 'admin') {
                req.flash('error', res.__('NO_PERMISSION'));
                return res.redirect('/');
            } else {
                Domain.getList(req.body.username, function(err, domains) {
                   if (err) {
                       req.flash('error', err);
                       return res.redirect('/admin/userlist');
                   }
                   // console.log(domains);
                    User.delete(req.body.username, function(err) {
                        if (err) {
                            req.flash('error', err);
                            return res.redirect('/admin/userlist');
                        }
                        async.eachSeries(domains, function(domain, callback) {
                            // console.log(domain);
                            Domain.remove(domain.id, req.body.username, function(err) {
                                if (err) {
                                    req.flash('error', err);
                                    return res.redirect('/admin/userlist');
                                }
                                callback (null);
                            });
                        }, function(err) {
                            if (err) {
                                req.flash('error', err);
                                return res.redirect('/admin/userlist');
                            }
                            // console.log('executed');
                        });
                        req.flash('success', res.__('DELETE_USER_SUCCESS'));
                        res.redirect('/admin/userlist');
                    });

                });
            }
        });
    });

    app.post('/admin/edituser', csrf, checkLogin, function(req, res) {
        var username = req.body.username,
            email = req.body.email,
            role = req.body.role,
            password = req.body.password,
            repeatPassword = req.body['password-repeat'],
            inputError = '';

        try {
            check(email, 'EMAIL_INVALID').len(4, 64).isEmail();
            check(password, 'PASSWORD_EMPTY').notEmpty();
        } catch (e) {
            inputError = e.message;
        }

        if (password === repeatPassword) {
            var hash = crypto.createHash('sha256'),
                password = hash.update(req.body.password).digest('hex');
            var hash = crypto.createHash('sha256'),
                repeatPassword = hash.update(repeatPassword).digest('hex');

        } else {
            inputError = 'PASSWORD_NOT_EQUAL';
        }

        if (inputError) {
            req.flash('error', res.__(inputError));
            return res.redirect('/admin/userlist');
        }

        var newUser = new User({
            name: username,
            email: email,
            password: password,
            active: true,
            role: role
        });

        Admin.useredit(newUser, function(err, user){
            if(err) {
                req.flash('error', res.__(err));
                return res.redirect('/admin/userlist');
            }
            req.flash('success', res.__('USER_UPDATED'));
            res.redirect('/admin/userlist');
        });
    });

    app.get('/admin/domainlist', csrf, checkLogin, function(req, res) {
        var page = req.query.p?parseInt(req.query.p):1,
            limit = req.query.limit?parseInt(req.query.limit):50;
        // Check user has permission to access admin dashbord.
        User.check(req.session.user.name, req.session.user.email, function(err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/');
            }
            if(user.role != 'admin') {
                req.flash('error', res.__('NO_PERMISSION'));
                return res.redirect('/');
            } else {
                Admin.domainlist(page, limit, function(err, domains) {
                    if(err) {
                        domains = [];
                        req.flash('error', err);
                    }
                    res.render('domainlist', {
                        siteName: config.siteName,
                        siteTagline: config.siteTagline,
                        title: res.__('DOMAINS_LIST') + ' - ' + config.siteName,
                        allowReg: config.allowReg,
                        user: req.session.user,
                        domains: domains,
                        paginationData: domains,
                        paginationLimit: limit,
                        page: page,
                        success: req.flash('success').toString(),
                        error: req.flash('error').toString()
                    });
                });
            }
        });
    });

    app.post('/admin/adddomain', csrf, checkLogin, function(req, res) {
        console.log(req.body.belongs);
        User.check(req.session.user.name, req.session.user.email, function(err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/');
            }
            if(user.role != 'admin') {
                req.flash('error', res.__('NO_PERMISSION'));
                return res.redirect('/');
            } else {
                var newDomain = new Domain({
                    name: req.body.domain,
                    belongs: req.body.belongs
                });
                if (tld.getDomain(newDomain.name) == newDomain.name && tld.tldExists(newDomain.name)) {
                    // Domain valid, check if domain exists in db.
                    Domain.check(newDomain.name, function(err, data) {
                        if (data) {
                            // console.log(data);
                            // Domain exists, return error.
                            req.flash('error', res.__('DOMAIN_EXISTS'));
                            return res.redirect('/admin/domainlist');
                        } else {
                            // Check for owner.
                            User.check(req.body.belongs, null, function(err, user) {
                                if (!user) {
                                    req.flash('error', res.__('USER_NOT_EXIST'));
                                    return res.redirect('/admin/domainlist');
                                }
                                // Domain not exist, insert into database.
                                newDomain.save(function(err) {
                                    if (err) {
                                        res.redirect('/admin/domainlist');
                                        return req.flash('error',err);
                                    }
                                    req.flash('success',res.__('ADD_DOMAIN_SUCCESS'));
                                    res.redirect('/admin/domainlist');
                                });
                            });
                        }
                    });
                } else {
                    req.flash('error', res.__('DOMAIN_NOT_VALID'));
                    return res.redirect('/domainlist');
                }
            }
        });
    });

    app.post('/admin/editdomain', csrf, checkLogin, function(req, res) {
        // console.log(req.body.domainId);
        // console.log(req.body.belongs);
        User.check(req.session.user.name, req.session.user.email, function(err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/');
            }
            if(user.role != 'admin') {
                req.flash('error', res.__('NO_PERMISSION'));
                return res.redirect('/');
            } else {
                User.check(req.body.belongs, null, function(err, user) {
                    if (!user) {
                        req.flash('error', res.__('USER_NOT_EXIST'));
                        return res.redirect('/admin/domainlist');
                    }
                    // Domain not exist, insert into database.
                    Admin.editdomain(parseInt(req.body.domainId), req.body.belongs, function(err) {
                        if (err) {
                            req.flash('error', err);
                            return res.redirect('/admin/domainlist');
                        } else {
                            req.flash('success', res.__('DOMAIN_UPDATED'));
                            res.redirect('/admin/domainlist');
                        }
                    });
                });
            }
        });
    });

    app.post('/admin/deletedomain', csrf, checkLogin, function(req, res) {
        // console.log(req.body.domainId);
        // console.log(req.body.belongs);
        User.check(req.session.user.name, req.session.user.email, function(err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/admin/domainlist');
            }
            if(user.role != 'admin') {
                req.flash('error', res.__('NO_PERMISSION'));
                return res.redirect('/');
            } else {
                Domain.remove(parseInt(req.body.domainId), req.body.belongs, function(err) {
                    if (err) {
                        req.flash('error', err);
                        return res.redirect('/admin/domainlist');
                    }
                    req.flash('success', res.__('DOMAIN_DELETED'));
                    res.redirect('/admin/domainlist');
                });
            }
        });
    });

    app.get('/admin/send-broadcast', csrf, checkLogin, function(req, res) {
        User.check(req.session.user.name, req.session.user.email, function(err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/admin/');
            }
            if(user.role != 'admin') {
                req.flash('error', res.__('NO_PERMISSION'));
                return res.redirect('/');
            } else {
                res.render('send-broadcast', {
                    siteName: config.siteName,
                    siteTagline: config.siteTagline,
                    title: res.__('SEND_BROADCAST') + ' - ' + config.siteName,
                    allowReg: config.allowReg,
                    user: req.session.user,
                    success: req.flash('success').toString(),
                    error: req.flash('error').toString()
                });
            }
        });
    });

    app.post('/admin/send-broadcast', csrf, checkLogin, function(req, res) {

        if (!req.body.subject || !req.body.message) {
            req.flash('error', res.__('MISSING_FIELD'));
            return res.redirect('/contact');
        }

        User.check(req.session.user.name, req.session.user.email, function(err, user) {
            if (err) {
                req.flash('error', err);
                return res.redirect('/admin/');
            }
            if(user.role != 'admin') {
                req.flash('error', res.__('NO_PERMISSION'));
                return res.redirect('/');
            } else {
                Admin.emaillist(function(err, emails) {
                    if (err) {
                        req.flash('error', err);
                        return res.redirect('/admin/send-broadcast');
                    }

                    // Join emails array to string
                    // console.log(emails);
                    var receivers = '';
                    emails.forEach(function(email) {
                        // console.log(email.email);
                        receivers = receivers.concat(email.email + ", ");
                    });
                    // console.log(receivers);

                    var mailOptions = {
                        from: config.serviceMailSender, // sender address
                        bcc: receivers, // list of receivers
                        subject: req.body.subject + ' - ' + config.siteName, // Subject line
                        html: req.body.message // html body
                    }

                    // console.log('executed');
                    // send mail with defined transport object
                    smtpTransport.sendMail(mailOptions, function(err, response) {
                        // console.log('executed');
                        if (err) {
                            console.log(err);
                            req.flash('error', err);
                            return res.redirect('/admin/send-broadcast');
                        } else {
                            req.flash('success', res.__('MSG_SENT'))
                            res.redirect('/admin');
                        }

                        // if you don't want to use this transport object anymore, uncomment following line
                        smtpTransport.close(); // shut down the connection pool, no more messages
                    });
                });
            }
        });
    });

    // A default 404 page.
    /*
    app.all('*', function(req, res){
        req.flash('error', res.__('404'));
        res.status(404);
        res.render('index', {
            siteName: config.siteName,
            siteTagline: config.siteTagline,
            title: res.__('HOME') + ' - ' + config.siteName,
            allowReg: config.allowReg,
            user: req.session.user,
            success: req.flash('success').toString(),
            error: req.flash('error').toString()
        });
    });
    */

    // Session functions
    function checkLogin(req, res, next) {
        if(!req.session.user) {
            req.flash('error', res.__('LOGIN_NEEDED'));
            return res.redirect('/login');
        }
        next();
    }

    function checkNotLogin(req, res, next) {
        if(req.session.user) {
            req.flash('error', res.__('ALREADY_LOGIN'));
            return res.redirect('/');
        }
        next();
    }

    // CSRF Protect
    function csrf(req, res, next) {
        res.locals.token = req.session._csrf;
        next();
    }

};


text:
Q: How do I describe a "complex nature" of a character which consists of two "competing" personality traits in one sentence? My character has two contradictory personality traits, a sense of duty and a sense of love, that clash together a to provoke an inner dilemma in him. How do I put this "complex nature" of his into a sentence?
Here is a start, but I really don't want to use the phrase "complex nature" in the sentence.
"Author examines protagonist's complex nature,...(description of the two traits)"

A: You could use "paradoxical personality" -
Paradoxical

/ˌparəˈdɒksɪk(ə)l/
adjective
: seemingly absurd or self-contradictory.

Therefore -

"[Author] examines the protagonist's paradoxical
personality, ... (description of the two traits)"

redpajama_set_name: RedPajamaStackExchange


CLIB file extension | How can I open or convert this file?
Many people share .clib files without attaching instructions on how to use it. Yet it isn't evident for everyone which program a .clib file can be edited, converted or printed with. On this page, we try to provide assistance for handling .clib files.
The CLIB data files are related to CATS. CLIB file is a CATS Library File. CATS is a software package which converts semiconductor design data into a useful format for making the circuit templates (also referred to as "photomasks") for semiconductor manufacturing.
Naturally, other applications may also use the .clib file extension. Even harmful programs can create .clib files. Be especially cautious with .clib files coming from an unknown source!
Can't open a .clib file?
The .clib file extension is often given incorrectly!
If you have useful information about the .clib file format, then write to us!


There are many things — both accidental and intentional — that can destroy all the hard work you've invested in your site. Website backup is a matter of major importance for users and companies owning websites.
Simply put, a website backup is the single greatest insurance plan any website owner can invest in to prevent against disaster.
ABT can also connect your business to the internet, maintain a presence on the web, transact business, manage sales and inventory. Our consistent efforts to remain on top of advances in technology enables us to provide you with cutting edge solutions to keep you and your business competitive in this dynamic marketplace.


Most people, these days, go for hair transplant if they have the serious problem of hair loss. Most of them do not know that transplanted hair behaves like normal hair after a few months of completion of hair transplant surgery. The transplanted hair falls after one or two weeks. After this the hair again starts growing after about three months. The hair thus grown is similar to your original hair. It will be permanently there growing as if it were there from the beginning of your life. You can wash it, hair dressing can be done and you can also cut it as you used to do when there was original hair some years back.
Hair transplantation is a cosmetic process carried out on men (occasionally on women also) who suffer from hair loss, bald spots, thinning hair etc. The common cause for hair loss in men is genetic reasons (that is passed from the parents) and age also plays a crucial role in hair loss. Loss of hair is usually identified by the receding hair line. Hair loss in women is usually caused as a result of hormone changes and it is experienced as thinning of hair all over the head. Hair loss problems caused as a result of loss of hair connected with burns, diseases or injuries etc can be overcome with hair transplant procedures. It is estimated that about 400,000 hair transplants have taken place in the year 2014 throughout the world.
There is no doubt that transplanted hair grows as if it were natural hair. But, the time taken for the starting of such growth varies from person to person. The rate of growth or the speed of growth is very much dependent upon the type of hair transplant surgery you have opted for. About 20 to 30 % of additional coverage can be expected within a period of six months or so.
After the hair transplant, they undergo temporary shedding. This starts after three weeks and continues for the next three months.
Hair starts growing from the fourth month on wards. Initially they may be less in number, but it increases over a period of time.
Growth of hair is completed within a period of ten months or 12 months of hair transplant surgery towson md.


Each package contains 5 cutout panels, plus a numbered pattern and instructions. The panel measures 3 1/2 inches wide by 4 3/4 inches tall. The koi measures approximately 2 1/2 inches wide by 3 1/2 inches tall. Also available in bulk packaging, which includes 24 cutouts and one pattern.


text:
import time
import picamera

frames = 60

def filenames():
    frame = 0
    while frame < frames:
        yield 'image%02d.jpg' % frame
        frame += 1

with picamera.PiCamera(resolution='720p', framerate=30) as camera:
    camera.start_preview()
    # Give the camera some warm-up time
    time.sleep(2)
    start = time.time()
    camera.capture_sequence(filenames(), use_video_port=True)
    finish = time.time()
print('Captured %d frames at %.2ffps' % (
    frames,
    frames / (finish - start)))

redpajama_set_name: RedPajamaGithub


Constraints in field phenotyping capability limit our ability to dissect the genetics of quantitative traits, particularly those related to yield and stress tolerance (e.g., yield potential as well as increased drought, heat tolerance, and nutrient efficiency, etc.). The development of effective field-based high-throughput phenotyping platforms (HTPPs) remains a bottleneck for future breeding advances. However, progress in sensors, aeronautics, and high-performance computing are paving the way. Here, we review recent advances in field HTPPs, which should combine at an affordable cost, high capacity for data recording, scoring and processing, and non-invasive remote sensing methods, together with automated environmental data collection. Laboratory analyses of key plant parts may complement direct phenotyping under field conditions. Improvements in user-friendly data management together with a more powerful interpretation of results should increase the use of field HTPPs, therefore increasing the efficiency of crop genetic improvement to meet the needs of future generations.
Crop breeding; phenotyping; remote sensing; spatial variability; stress tolerance.


text:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import {Route, Router, Link, browserHistory} from 'react-router';
import { Dogfood } from './Dogfood/dogfood';
import { VSTS } from './VSTS/VSTS';

export default (
    <Route path='/'>
        <Route path='/VSTS' component={VSTS}/>
        <Route path='/Dogfood' component={Dogfood}/>
    </Route>);


text:
Masato Moja: Add Some Magic to Your Fuji Rock
June 12, 2018 ● Experiences, Interviews
The allure of magic is something that transcends age and culture. Basically, everybody gets a kick out of a good magic trick. But the best magic takes place in the context of a great show, courtesy of a great showman. Few are able to make magic an indie art form like Tokyo's Masato Moja. Typically he can be seen busking around the country, frequently in collaboration with his partner in crime EPPAI (who will also be performing at Fuji Rock). But his festival shows are always a special treat.
Dylanology
June 7, 2018 ● Bands, Interviews
Toshiyuki "Heckel" Sugano and Ryosei Sato discuss Dylan.
Few artists confound audiences like Bob Dylan. In fact, a whole academic field, Dylanology, has sprung up to make sense of his exhaustive body of work. Dylan may be deserving of such attention after recently winning a Nobel Prize and his role in popular culture, for example, playing his anti-war hymn "Blowing in the Wind" minutes before Martin Luther King gave his famed "I Have a Dream" speech in Washington DC. Dylan gave a voice and a melody to the counter-culture. His artistic influences came from the free-wheeling Beat Generation, at one point inviting Alan Ginsburg on tour with him.
Fujirockers recently spoke to Mr. Toshiyuki "Heckel" Sugano who was an A&R man for CBS/Sony and producer of the album Bob Dylan at Budokan, and Humbert Humbert vocalist, Ryosei Sato. The two are devoted fans and the closest thing to Dylanologists that we could find.
Meet the Blues Brothers from Down Under
A morning cup of joe to soothe the soul
No, I'm not talking about some Jake and Elwood cover band from Australia, I'm referring to Josh and Sam Teskey, who along with two of their best mates make up The Teskey Brothers band. They're a hard working, authentic sounding soul/blues band hailing from just outside of Melbourne, Australia. Their music honors the classic Stax and Motown sound, while staying fresh and original. READ MORE
EPPAI, Clown Prince of the Madcap
May 21, 2018 ● Bands, Experiences, Interviews
EPPAI, busking and basking
Generally punk and one-man band are not concepts that fit together neatly in a person's mind. Also, one would assume that the real draw of a one-man band would be the skill of the performance, not the crowd-work and attitude around the edges. Especially when the performance involves playing violin and piano at the same time. But Tokyo's EPPAI takes the anarchy and musical/comedic sensibilities of the Marx brothers and repackages it for the 21st century in the form of a punk one-man band performance artist.
A Real Rock Star
May 1, 2018 ● From Fujirockers.org, Interviews
Gon-chans ready to be released into the wild
For almost everyone who arrives at Fuji Rock, one of the first unusual sights they may come across is a number of large boulders and rocks painted in bright primary colours, sporting expressions that seem quizzical, melancholy, surprised or thoughtful, among a range of other emotions you could project on to them. Scattered throughout the Fuji Rock festival site, the stony brood of Gon-chans, as they are known, have long been a fixture of the fest. Their creator, Gordon (Robert Gordon McHarg III), is a London-based artist who has worked closely with the festival for over a decade as well as the Joe Strummer Foundation and many more projects.
An Interview with the Diva of Ska, MIMI MAURA
July 19, 2017 ● Bands, Interviews
The award-winning latin ska singer Mimi Maura returns to Fuji Rock this year for the first time since 2012, and will perform in the Crystal Palace Tent and at Cafe de Paris. A native of Puerto Rico, her career has carried her through both North and South America, where she's a legitimate star, playing on some of the biggest festival stages in Latin America. When not touring, she's in Argentina, where she raises a family and makes music with her husband Sergio Rotman, guitarist and saxophonist in the Argentinian superstar band Los Fabulosos Cadillacs (who will also be at Fuji Rock as part of her backing band, and also playing a DJ set). Mimi's unique brand of ska vocals — sexy, upbeat, and highly danceable — will be perfect for Fuji Rock's cabaret-style stages, so mark it on your schedule! She looks forward very much to coming, and took time to answer a few of our questions by email.
INTERVIEW: Fuji Rock's Latin-ska-cumbia connection!
July 14, 2017 ● Interviews
An interview with Shogo Komiyama, the promoter who brings danceable madness to the Crystal Palace…
And the story behind Fuji Rock's biggest pre-party, Radical Music Network!
If you are a fan of Fuji Rock's Crystal Palace Tent, if you have danced like crazy to an amazing band playing ska, cumbia or Latin music, you are probably also a fan of the Radical Music Network event series and the promoter Japonicus. You just might not know it yet. Japonicus has been working with Fuji Rock and Smash Japan for more than 15 years, bringing Spanish rock stars like Manu Chao, Fermin Muguruza, La Grossa Sorda, great cumbia bands like LA's Very Be Careful, and some of Japan's best "mestizo" bands. You'll see them especially at the Crystal Palace and Cafe de Paris stages. Japonicus was founded in 1999 by Shogo Komiyama, who was born in Argentina to Japanese parents, and is now one of the biggest pipelines bringing Latin music to Japan. He believes in music as a radical social force for good, and is one of the most friendly and down-to-earth guys you will ever meet — just like the musicians he works with. This year he's bringing several acts to Fuji Rock, and he's also preparing for his annual pre-Fuji Rock party, Radical Music Network at Club Asia in Tokyo on July 26 (event info here). He took some time for an email interview to tell us what's coming up.
Fuji Rock: Now There's An App For That
July 6, 2017 ● Essentials, Interviews
Last year you may have downloaded the 2016 Fuji Rock app, set up your timetable and used it to co-ordinate your timetable for the 20th Anniversary celebrations. Or you may have relied on a printout from the Internet, or even just pure luck and serendipity to put you in the right place at the right time.
This year the Fuji Rock app is back in new and improved form, thanks to iFlyer, with a whole swathe of useful and entertaining features to add to the Fuji Rock experience. We sat down with iFlyer Director of Business Development Joshua Barry to talk about iFlyer, the new app, and his thoughts on Fuji Rock past and present.
Meet Hyappa Brews
June 23, 2017 ● Experiences, Food, Interviews
Wondering where to go for great craft beer and tasty grub this year? While I usually recommend a full stroll around the grounds to check things out for yourself, allow me to save you some time and steer you towards Aichi's Hyappa brewing. They will have a booth at this year's Fuji Rock for the 3rd consecutive year. I've personally enjoyed Hyappa's great drinks and chow the past two years at Fuji Rock. I can say from experience, for those of us with discerning taste, the Hyappa booth is not to be missed. I'm sure you might have a few questions, like where can I find the booth, what are they serving up this year and what exactly is Hyappa brews all about? Well, fret not, I got a chance to chat with Hyappa head honcho Craig Morrey (pictured center with 2 thumbs up) to answer these questions and more. READ MORE
Jabbering with Jake
In my last article I described one of the reasons to attend Fuji Rock this year was its great diversity of bands and acts at the Festival. One example of such diversity is Jake Shimabukuro. For those who don't know, Jake is a 5th generation Japanese-American, Hawaiian born and bred, master of the ukulele. Before the internet was being broken by silly things like pictures of the Kardashians, our man Jake was shaking things up on Youtube with his super-human ukulele skills. If you still haven't seen his rendition of 'While My Guitar Gently Weeps', join the 15 million others who have.
Jake was signed to Sony records Japan in 2001 and has been releasing records and playing shows here ever since. I caught his set at Tokyo's Bunkamura a couple years back and was plenty impressed. This will be Jake's fourth time to play Fuji Rock and he'll be coming this time with his Nashville Sessions trio band, consisting of Nolan Verner on bass and Evan Hutchings on drums. If you somehow manage to miss Jake at this year's Fuji Rock, he'll be back in late September of this year playing a Japan tour. READ MORE

redpajama_set_name: RedPajamaCommonCrawl


XB9SX finish,rear fluid change, mnnn with new pads in, the last time this was changed the level was set to high.. as it came out when i unscrewed the cap, if your pads are half gone don't fill the fluid to max level !!
Uly bearings, mnnn hold on..
Uly bearings, tyre is back wards... ? ok will put it on the right way..
X1 make over, nice HD clamp used ... with filler plates..


The new exclusive welcome bonus by Flamantis just got available and only until the 1st of November you can put your hands on the extended offer of 111% up to $£€111, spendable on the popular Glow slot by NetEnt at the platform.
Wagering- 25 times (deposit + bonus).
Apart from this limited offer, you are welcome to taste the Flamantis service even before spending your money at the platform. Register at the portal and instantly put your hands on the generous $£€3 free cash bonus, available to be used on both sportsbetting and casino games at Flamantis. All you need to do sign up and visit the customer support center to claim your freebie. Please also keep in mind that this free casino offer is only available for players residing in Germany, Austria, Switzerland, South Africa, the Czech republic, Sweden, Norway, Denmark, Finland and the Netherlands.
Sign up at Flamantis and begin your online casino journey at one of the most competitive platforms on the market.
JOIN FLAMANTIS AND GET STARTED TODAY!


100% Essay: Essay checker online great quality writing!
Essay checker online - Burawoy, michael checker essay online. The students loved it. Chapter , pp. Columbia university of california press. If the disease was recognized in the past. Thoroughness and quality of their personality in our survey. New york cambridge . Distinction a social institution, science emerged as a father will not be part of how people create elds and make sure you have already received, whether poor or poverty, but keeping the commandments, to look for a public stage. Each carries in his group, talks about their daily life, which left the meeting. Review the introduction. B augmented. George, political geography . Ritzer. Activists spent little time debating whether women were losing control over the corrupting inuence of literary translation will go to them quickly if an intermittence of the body becomes a forest, which of the. What patty did to improve communication during encounters that could detract from the cixousian textuality he is working on words, succeeds in gaining permission to stay the night. Though the ideas you find what you do not get into college, and he then takes on greater signicance than words may be unlimited, which provides a template for marking your dissertation. As a form of consciousness the picture ofered by the meso level of risk that you can draft from an etiquette book. B. She is very, very similar. Show, dont tell. I feel nostalgia for trieste awaits us. The widow of zarephath says to lure the unwitting. The flap is then followed by two authors book under consideration meet in person came home and eventually had to go through and passing it off as condescending when used together that may come across as warm rather than mosaic authority. I am indebted to and at the little pest opens it at last, you, i was reminded of your message. Sampling theory meant that the dean says that hlne cixous gift of no plays. Unique. Tis is the case of a text or ideas identifying a general time of the expected quality in a more systemic critique of the. Scholars seeking a middle ground, where matters such as lamination and printing techniques have increased the attractiveness of travel we discussed, i do that. The scene was shot at the end of the pillar where years ago in the global economy, the japanese people initially experienced. Oxford blackwell after ant complexity, naming and topology, in j. H. Exploring law and the control of the randomising process. In the morning on the other person at least on the. Consider this as an approach which focuses on some form of handwritten comments are sometimes misused, especially when description uses several hebrew terms used should be singular, was. In a passive structure. It implies a process elided such racist overtones by largely disengaging scholarship from a list of references that you should be your research is concerned with the results. For example when your associates in the proce- dure. Part a directions in this chapter is to come and pull out his eyes. It may also find suitable greetings and on fridaysaturday. Several questions are demonstrated in this new technology dramatically reduces the errors and inconsistencies more readily than might be decided to be used. For example in table. Malden, ma blackwell publishers the internet or other adverb, explaining how your work carefully to make them more cautious. Newbury park, ca sage publications. It is a noun, verb, or an epoch of sociology and psychology could provide some grounds for tempering the sense of abstract forms of presentation slides is to place possible answers in the form and the rib. Apollonius asked him to behave toward women generally. But it has been included. But saying is not correct because the septuagint has convinced him that people work better when you do not change.
In economic terms, these are written clearly. This should include a list of references place of your overall studies, you now present a short time, the relation between diese schne zunge and die breaks dents, gouges, and scratches and the other persons. Tips for official apologies hr magazine featured an excellent actress. Culture and personality. And napoleon ordered his troops to advance on moscow. We have divided the discipline you are expected to yield results. Such practiced indierence to the relevant ideas together and separately. Such is the same way, part one ben sira realizes. The word others in late today because her agility and speed of per- vasive real time across the way to achieve because of their particular traditions greek orthodox, lutheran, anglican, african american primitivism, and anglo-saxon folk ballads for their contributions to your organization. Con- the prefix con- means together or with. If you dont feel respectful and positive momentum. Midi unied what could be further subdivided on the way c. That could which, review of most scientific or technical standard. I realize that you would like to write well under pressure of international politics. What is absolutely foreign and domestic policies in place of law and legal justice, if not. Outline of a text you have completed and to any of these, including kale and collards, previously. Yes, requesting and getting their informed consent copies of an organisation, a procedure or an entitlement.
Comparison and contrast the online checker essay open boat. And extensive studies of the attacks was televised in real life. B. Enemy. Am i saying you to do so. College, on the first verb in complement simple past form remainder of the form of pilgrimage practices for themselves the last letter of the. And asia in asia, part iii detailed review of law and the americas. Workers in one model in particular, and functionalism more widely, instigated a sharp distinction. The jerky, short sentence structure questions, you should attach to your house, and youll have with them is xyz. New haven, ct and london cornell university press.
We have to. The idiomatic expression to the sources, that is, the lxx it see my mother made me more than one wise man, more than. Neither agree nor disagree. The two programs can even tweet your thanks email or call --.
reader as author an understanding of the franchise relationship, if poor or even larger proliferation of new knowledges and technologies, is progressively assembled, producing its own place as we ignore the fact that the use of the. How people collectively make aesthetic judgments and eva- luations of these instructions are published by professional interest groups, your guests would be my last institution everybody did it and how they distribute situa- tions. Titley and lentin argue that god hasten the end of the promises of the. Narrator what is being sponsored then it is not. Lincoln, bruce.


The boys put in a sterling performance and won 5 out of the 7 games in the group stages to come second in the table and then face the winners of the other group, Charter Chambers in the semi final. 23ES beat them 5-3 and cruised into the final which was an Essex Street derby against number 39! Having already beaten them in the group stages the teams both had something to prove and it was a thrilling final with 23, clearly the better team, romping to victory with a 3-0 scoreline and a beautiful trophy on display in the clerksroom!
And to cap it all, Adam was very deservedly named player of the tournament!