diff --git a/lib/acp.js b/lib/acp.js index c5de8452..5bee1f28 100644 --- a/lib/acp.js +++ b/lib/acp.js @@ -11,169 +11,140 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI var Logger = require("./logger"); var Server = require("./server"); -var ActionLog = require("./actionlog"); +var db = require("./database"); -module.exports = { - init: function (user) { - var sv = Server.getServer(); - var db = sv.db; - ActionLog.record(user.ip, user.name, "acp-init"); - user.socket.on("acp-announce", function(data) { - ActionLog.record(user.ip, user.name, "acp-announce", data); - sv.announcement = data; - sv.io.sockets.emit("announcement", data); - if (sv.cfg["enable-ssl"]) - sv.ioSecure.sockets.emit("announcement", data); - }); +function handleAnnounce(user, data) { + var sv = Server.getServer(); - user.socket.on("acp-announce-clear", function() { - ActionLog.record(user.ip, user.name, "acp-announce-clear"); - sv.announcement = null; - }); + sv.announcement = { + title: data.title, + text: data.content, + from: user.name + }; - user.socket.on("acp-global-ban", function(data) { - ActionLog.record(user.ip, user.name, "acp-global-ban", data.ip); - db.setGlobalIPBan(data.ip, data.note, function (err, res) { - db.listGlobalIPBans(function (err, res) { - res = res || []; - user.socket.emit("acp-global-banlist", res); - }); - }); - }); - - user.socket.on("acp-global-unban", function(ip) { - ActionLog.record(user.ip, user.name, "acp-global-unban", ip); - db.clearGlobalIPBan(ip, function (err, res) { - db.listGlobalIPBans(function (err, res) { - res = res || []; - user.socket.emit("acp-global-banlist", res); - }); - }); - }); - - db.listGlobalIPBans(function (err, res) { - res = res || []; - user.socket.emit("acp-global-banlist", res); - }); - - user.socket.on("acp-lookup-user", function(name) { - db.users.search(name, ["name", "email", "global_rank"], function (err, res) { - res = res || []; - user.socket.emit("acp-userdata", res); - }); - }); - - user.socket.on("acp-lookup-channel", function (data) { - db.searchChannel(data.field, data.value, function (e, res) { - res = res || []; - user.socket.emit("acp-channeldata", res); - }); - }); - - user.socket.on("acp-reset-password", function(data) { - db.users.getGlobalRank(data.name, function (err, rank) { - if(err || rank >= user.global_rank) - return; - - db.genPasswordReset(user.ip, data.name, data.email, - function (err, hash) { - var pkt = { - success: !err - }; - - if(err) { - pkt.error = err; - } else { - pkt.hash = hash; - } - - user.socket.emit("acp-reset-password", pkt); - ActionLog.record(user.ip, user.name, - "acp-reset-password", data.name); - }); - }); - }); - - user.socket.on("acp-set-rank", function(data) { - if(data.rank < 1 || data.rank >= user.global_rank) - return; - - db.users.getGlobalRank(data.name, function (err, rank) { - if(err || rank >= user.global_rank) - return; - - db.users.setGlobalRank(data.name, data.rank, - function (err, res) { - ActionLog.record(user.ip, user.name, "acp-set-rank", - data); - if(!err) - user.socket.emit("acp-set-rank", data); - }); - }); - }); - - user.socket.on("acp-list-loaded", function() { - var chans = []; - var all = sv.channels; - for(var c in all) { - var chan = all[c]; - - chans.push({ - name: chan.name, - title: chan.opts.pagetitle, - usercount: chan.users.length, - mediatitle: chan.playlist.current ? chan.playlist.current.media.title : "-", - is_public: chan.opts.show_public, - registered: chan.registered - }); - } - - user.socket.emit("acp-list-loaded", chans); - }); - - user.socket.on("acp-channel-unload", function(data) { - if(sv.isChannelLoaded(data.name)) { - var c = sv.getChannel(data.name); - if(!c) - return; - ActionLog.record(user.ip, user.name, "acp-channel-unload"); - c.initialized = data.save; - // copy the list of users to prevent concurrent - // modification - var users = Array.prototype.slice.call(c.users); - users.forEach(function (u) { - c.kick(u, "Channel shutting down"); - }); - - // At this point c should be unloaded - // if it's still loaded, kill it - if(sv.isChannelLoaded(data.name)) - sv.unloadChannel(sv.getChannel(data.name)); - } - }); - - user.socket.on("acp-actionlog-list", function () { - ActionLog.listActionTypes(function (err, types) { - if(!err) - user.socket.emit("acp-actionlog-list", types); - }); - }); - - user.socket.on("acp-actionlog-clear", function(data) { - ActionLog.clear(data); - ActionLog.record(user.ip, user.name, "acp-actionlog-clear", data); - }); - - user.socket.on("acp-actionlog-clear-one", function(data) { - ActionLog.clearOne(data); - ActionLog.record(user.ip, user.name, "acp-actionlog-clear-one", data); - }); - - user.socket.on("acp-view-stats", function () { - db.listStats(function (err, res) { - if(!err) - user.socket.emit("acp-view-stats", res); - }); - }); + sv.io.sockets.emit("announcement", sv.announcement); + if (sv.ioSecure) { + sv.ioSecure.sockets.emit("announcement", sv.announcement); } } + +function handleAnnounceClear(user) { + Server.getServer().announcement = null; +} + +function handleGlobalBan(user, data) { + db.globalBanIP(data.ip, data.note, function (err, res) { + if (err) { + user.socket.emit("errMessage", { + msg: err + }); + return; + } + + db.listGlobalBans(function (err, bans) { + if (err) { + user.socket.emit("errMessage", { + msg: err + }); + return; + } + + var flat = []; + for (var ip in bans) { + flat.push({ + ip: ip, + note: bans[ip].reason + }); + } + user.socket.emit("acp-gbanlist", flat); + }); + }); +} + +function handleGlobalBanDelete(user, data) { + db.globalUnbanIP(data.ip, function (err, res) { + if (err) { + user.socket.emit("errMessage", { + msg: err + }); + return; + } + + db.listGlobalBans(function (err, bans) { + if (err) { + user.socket.emit("errMessage", { + msg: err + }); + return; + } + + var flat = []; + for (var ip in bans) { + flat.push({ + ip: ip, + note: bans[ip].reason + }); + } + user.socket.emit("acp-gbanlist", flat); + }); + }); +} + +function handleListUsers(user, data) { + var name = data.name; + if (typeof name !== "string") { + name = ""; + } + + var fields = ["id", "name", "global_rank", "email", "ip", "time"]; + + db.users.search(name, fields, function (err, users) { + if (err) { + user.socket.emit("errMessage", { + msg: err + }); + return; + } + user.socket.emit("acp-list-users", users); + }); +} + +function handleSetRank(user, data) { + +} + +function handleResetPassword(user, data) { + +} + +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(this, user)); + + db.listGlobalBans(function (err, bans) { + if (err) { + user.socket.emit("errMessage", { + msg: err + }); + return; + } + + var flat = []; + for (var ip in bans) { + flat.push({ + ip: ip, + note: bans[ip].reason + }); + } + user.socket.emit("acp-gbanlist", flat); + }); + Logger.eventlog.log("[acp] Initialized ACP for " + user.name + "@" + user.ip); +} + +module.exports.init = init; diff --git a/lib/user.js b/lib/user.js index 0de72d9a..a8da9934 100644 --- a/lib/user.js +++ b/lib/user.js @@ -5,6 +5,7 @@ var MakeEmitter = require("./emitter"); var db = require("./database"); var InfoGetter = require("./get-info"); var Config = require("./config"); +var ACP = require("./acp"); function User(socket) { var self = this; @@ -36,6 +37,19 @@ function User(socket) { self.initChannelCallbacks(); }); + self.socket.once("initACP", function () { + self.whenLoggedIn(function () { + if (self.global_rank >= 255) { + ACP.init(self); + } else { + self.kick("Attempted initACP from non privileged user. This incident " + + "will be reported."); + Logger.eventlog.log("[acp] Attempted initACP from socket client " + + self.name + "@" + self.ip); + } + }); + }); + self.socket.on("login", function (data) { data = (typeof data === "object") ? data : {}; diff --git a/lib/web/acp.js b/lib/web/acp.js index 3d672f07..b359803c 100644 --- a/lib/web/acp.js +++ b/lib/web/acp.js @@ -4,6 +4,7 @@ var webserver = require("./webserver"); var sendJade = require("./jade").sendJade; var Logger = require("../logger"); var db = require("../database"); +var Config = require("../config"); function checkAdmin(cb) { return function (req, res) { @@ -40,9 +41,18 @@ function checkAdmin(cb) { * Handles a request for the ACP */ function handleAcp(req, res, user) { + var sio; + if (req.secure) { + sio = Config.get("https.domain") + ":" + Config.get("https.port"); + } else { + sio = Config.get("http.domain") + ":" + Config.get("io.port"); + } + sio += "/socket.io/socket.io.js"; + sendJade(res, "acp", { loginName: user.name, - loggedIn: true + loggedIn: true, + sioSource: sio }); } diff --git a/lib/web/webserver.js b/lib/web/webserver.js index 62861893..66b2f386 100644 --- a/lib/web/webserver.js +++ b/lib/web/webserver.js @@ -116,13 +116,19 @@ function handleChannel(req, res) { loginName = req.cookies.auth.split(":")[0]; } - var iourl = ""; + var sio; + if (req.secure) { + sio = Config.get("https.domain") + ":" + Config.get("https.port"); + } else { + sio = Config.get("http.domain") + ":" + Config.get("io.port"); + } + sio += "/socket.io/socket.io.js"; + sendJade(res, "channel", { channelName: req.params.channel, loggedIn: loginName !== false, loginName: loginName, - sioSource: Config.get("http.domain") + ":" + Config.get("io.port") + - "/socket.io/socket.io.js" + sioSource: sio }); } diff --git a/templates/acp.jade b/templates/acp.jade index 232b0439..b0f81840 100644 --- a/templates/acp.jade +++ b/templates/acp.jade @@ -32,7 +32,7 @@ html(lang="en") #acp-announcements.col-md-12(style="display: none") h3 Announcements h3 New Announcement - .col-md-6 + div(style="max-width: 50%") form.form-horizontal(action="javascript:void(0)", role="form") .form-group label.control-label.col-sm-2(for="acp-announce-title") Title @@ -54,7 +54,7 @@ html(lang="en") th IP Address th Note h3 New Global Ban - .col-md-6 + div(style="max-width: 50%") form.form-horizontal(action="javascript:void(0)", role="form") .form-group label.control-label.col-sm-3(for="acp-gban-ip") IP Address @@ -76,10 +76,11 @@ html(lang="en") table.table.table-bordered.table-striped(style="margin-top: 20px") thead tr - th#acp-ulookup-id ID - th#acp-ulookup-name Name - th#acp-ulookup-rank Rank - th#acp-ulookup-email Email + th#acp-users-id ID + th#acp-users-name Name + th#acp-users-rank Rank + th#acp-users-email Email + th Actions #acp-channel-lookup.col-md-12(style="display: none") h3 Channels form.form-inline(action="javascript:void(0)", role="form") @@ -123,4 +124,10 @@ html(lang="en") include footer mixin footer() + script(type="text/javascript"). + var USEROPTS = { secure_connection: true }; + script(src=sioSource) + script(src="/sioconfig") + script(src="/assets/js/util.js") + script(src="/assets/js/paginator.js") script(src="/js/acp.js") diff --git a/www/js/acp.js b/www/js/acp.js index d90d5036..adb1bd6e 100644 --- a/www/js/acp.js +++ b/www/js/acp.js @@ -1,3 +1,18 @@ +(function () { + var opts = {}; + if (location.protocol === "https:") { + opts.secure = true; + } + window.socket = io.connect(IO_URL, opts); + window.socket.on("connect", function () { + window.socket.emit("initACP"); + }); + + window.socket.on("errMessage", function (data) { + alert(data.msg); + }); +})(); + function addMenuItem(target, text) { var ul = $("#nav-acp-section ul"); var li = $("
  • ").appendTo(ul); @@ -19,6 +34,7 @@ addMenuItem("#acp-loaded-channels", "Active Channels"); addMenuItem("#acp-eventlog", "Event Log"); addMenuItem("#acp-stats", "Stats"); +/* Log Viewer */ function readSyslog() { $.ajax(location.protocol + "//" + location.host + "/acp/syslog").done(function (data) { $("#acp-log").text(data); @@ -55,3 +71,159 @@ $("#acp-chanlog-name").keyup(function (ev) { readChanlog($("#acp-chanlog-name").val()); } }); + +/* Announcements */ +$("#acp-announce-submit").click(function () { + socket.emit("acp-announce", { + title: $("#acp-announce-title").val(), + content: $("#acp-announce-content").val() + }); +}); + +socket.on("announcement", function (data) { + $("#acp-announcements").find(".announcement").remove(); + + var al = makeAlert(data.title, data.text) + .removeClass("col-md-12") + .addClass("announcement") + .insertAfter($("#acp-announcements h3")[0]); + + al.find(".close").click(function () { + socket.emit("acp-announce-clear"); + }); + + $("#acp-announce-title").val(data.title); + $("#acp-announce-content").val(data.text); +}); + +/* Global bans */ +$("#acp-gban-submit").click(function () { + socket.emit("acp-gban", { + ip: $("#acp-gban-ip").val(), + note: $("#acp-gban-note").val() + }); +}); + +socket.on("acp-gbanlist", function (bans) { + var tbl = $("#acp-global-bans table"); + tbl.find("tbody").remove(); + + bans.forEach(function (b) { + var tr = $("").appendTo(tbl); + var td = $("").appendTo(tr); + var del = $("