var Logger = require("./logger"); var Server = require("./server"); var db = require("./database"); var util = require("./utilities"); import { v4 as uuidv4 } from 'uuid'; function eventUsername(user) { return user.getName() + "@" + user.realip; } function handleAnnounce(user, data) { var sv = Server.getServer(); sv.announce({ id: uuidv4(), title: data.title, text: data.content, from: user.getName() }); Logger.eventlog.log("[acp] " + eventUsername(user) + " opened announcement `" + data.title + "`"); } function handleAnnounceClear(user) { Server.getServer().announce(null); Logger.eventlog.log("[acp] " + eventUsername(user) + " cleared announcement"); } function handleGlobalBan(user, data) { const globalBanDB = db.getGlobalBanDB(); globalBanDB.addGlobalIPBan(data.ip, data.note).then(() => { Logger.eventlog.log("[acp] " + eventUsername(user) + " global banned " + data.ip); return globalBanDB.listGlobalBans().then(bans => { // Why is it called reason in the DB and note in the socket frame? // Who knows... const mappedBans = bans.map(ban => { return { ip: ban.ip, note: ban.reason }; }); user.socket.emit("acp-gbanlist", mappedBans); }); }).catch(error => { user.socket.emit("errMessage", { msg: error.message }); }); } function handleGlobalBanDelete(user, data) { const globalBanDB = db.getGlobalBanDB(); globalBanDB.removeGlobalIPBan(data.ip).then(() => { Logger.eventlog.log("[acp] " + eventUsername(user) + " un-global banned " + data.ip); return globalBanDB.listGlobalBans().then(bans => { // Why is it called reason in the DB and note in the socket frame? // Who knows... const mappedBans = bans.map(ban => { return { ip: ban.ip, note: ban.reason }; }); user.socket.emit("acp-gbanlist", mappedBans); }); }).catch(error => { user.socket.emit("errMessage", { msg: error.message }); }); } function handleListUsers(user, data) { var value = data.value; var field = data.field; value = (typeof value !== 'string') ? '' : value; field = (typeof field !== 'string') ? 'name' : field; var fields = ["id", "name", "global_rank", "email", "ip", "time"]; if(!fields.includes(field)){ user.socket.emit("errMessage", { msg: `The field "${field}" doesn't exist or isn't searchable.` }); return; } db.users.search(field, value, fields, function (err, users) { if (err) { user.socket.emit("errMessage", { msg: err }); return; } user.socket.emit("acp-list-users", users); }); } function handleSetRank(user, data) { var name = data.name; var rank = data.rank; if (typeof name !== "string" || typeof rank !== "number") { return; } if (rank >= user.global_rank) { user.socket.emit("errMessage", { msg: "You are not permitted to promote others to equal or higher rank than " + "yourself." }); return; } db.users.getGlobalRank(name, function (err, oldrank) { if (err) { user.socket.emit("errMessage", { msg: err }); return; } if (oldrank >= user.global_rank) { user.socket.emit("errMessage", { msg: "You are not permitted to change the rank of users who rank " + "higher than you." }); return; } db.users.setGlobalRank(name, rank, function (err) { if (err) { user.socket.emit("errMessage", { msg: err }); } else { Logger.eventlog.log("[acp] " + eventUsername(user) + " set " + name + "'s global_rank to " + rank); user.socket.emit("acp-set-rank", data); } }); }); } function handleResetPassword(user, data, ack) { var name = data.name; var email = data.email; if (typeof name !== "string" || typeof email !== "string") { return; } db.users.getGlobalRank(name, function (err, rank) { if (rank >= user.global_rank) { user.socket.emit("errMessage", { msg: "You don't have permission to reset the password for " + name }); return; } var hash = util.sha1(util.randomSalt(64)); var expire = Date.now() + 86400000; db.addPasswordReset({ ip: "", name: name, email: email, hash: hash, expire: expire }, function (err) { if (err) { ack && ack({ error: err }); return; } Logger.eventlog.log("[acp] " + eventUsername(user) + " initialized a " + "password recovery for " + name); ack && ack({ hash }); }); }); } function handleListChannels(user, data) { var field = data.field; var value = data.value; if (typeof field !== "string" || typeof value !== "string") { return; } var dbfunc; if (field === "owner") { dbfunc = db.channels.searchOwner; } else { dbfunc = db.channels.search; } dbfunc(value, function (err, rows) { if (err) { user.socket.emit("errMessage", { msg: err }); return; } user.socket.emit("acp-list-channels", rows); }); } function handleDeleteChannel(user, data) { var name = data.name; if (typeof data.name !== "string") { return; } var sv = Server.getServer(); if (sv.isChannelLoaded(name)) { sv.getChannel(name).users.forEach(function (u) { u.kick("Channel shutting down"); }); } db.channels.drop(name, function (err) { Logger.eventlog.log("[acp] " + eventUsername(user) + " deleted channel " + name); if (err) { user.socket.emit("errMessage", { msg: err }); } else { user.socket.emit("acp-delete-channel", { name: name }); } }); } function handleListActiveChannels(user) { user.socket.emit("acp-list-activechannels", Server.getServer().packChannelList(false, true)); } function handleForceUnload(user, data) { var name = data.name; if (typeof name !== "string") { return; } var sv = Server.getServer(); if (!sv.isChannelLoaded(name)) { return; } var chan = sv.getChannel(name); var users = Array.prototype.slice.call(chan.users); chan.emit("empty"); users.forEach(function (u) { u.kick("Channel shutting down"); }); Logger.eventlog.log("[acp] " + eventUsername(user) + " forced unload of " + name); } function init(user) { var s = user.socket; s.on("acp-announce", handleAnnounce.bind(this, user)); s.on("acp-announce-clear", handleAnnounceClear.bind(this, user)); s.on("acp-gban", handleGlobalBan.bind(this, user)); s.on("acp-gban-delete", handleGlobalBanDelete.bind(this, user)); s.on("acp-list-users", handleListUsers.bind(this, user)); s.on("acp-set-rank", handleSetRank.bind(this, user)); s.on("acp-reset-password", handleResetPassword.bind(this, user)); s.on("acp-list-channels", handleListChannels.bind(this, user)); s.on("acp-delete-channel", handleDeleteChannel.bind(this, user)); s.on("acp-list-activechannels", handleListActiveChannels.bind(this, user)); s.on("acp-force-unload", handleForceUnload.bind(this, user)); const globalBanDB = db.getGlobalBanDB(); globalBanDB.listGlobalBans().then(bans => { // Why is it called reason in the DB and note in the socket frame? // Who knows... const mappedBans = bans.map(ban => { return { ip: ban.ip, note: ban.reason }; }); user.socket.emit("acp-gbanlist", mappedBans); }).catch(error => { user.socket.emit("errMessage", { msg: error.message }); }); Logger.eventlog.log("[acp] Initialized ACP for " + eventUsername(user)); } module.exports.init = init;