diff --git a/channel.js b/channel.js
index 65c25fb8..6741b536 100644
--- a/channel.js
+++ b/channel.js
@@ -93,7 +93,9 @@ var Channel = function(name) {
};
this.ipbans = {};
this.namebans = {};
- this.logins = {};
+ this.ip_alias = {};
+ this.name_alias = {};
+ this.login_hist = [];
this.logger = new Logger.Logger("chanlogs/" + this.name + ".log");
this.i = 0;
this.time = new Date().getTime();
@@ -205,10 +207,6 @@ Channel.prototype.loadDump = function() {
this.motd = data.motd;
this.broadcastMotd();
}
- data.logins = data.logins || {};
- for(var ip in data.logins) {
- this.logins[ip] = data.logins[ip];
- }
this.setLock(!(data.openqueue || false));
this.chatbuffer = data.chatbuffer || [];
for(var i = 0; i < this.chatbuffer.length; i++) {
@@ -242,7 +240,6 @@ Channel.prototype.saveDump = function() {
permissions: this.permissions,
filters: filts,
motd: this.motd,
- logins: this.logins,
openqueue: this.openqueue,
chatbuffer: this.chatbuffer,
css: this.css,
@@ -323,11 +320,9 @@ Channel.prototype.saveRank = function(user) {
Channel.prototype.getIPRank = function(ip) {
var names = [];
- if(this.logins[ip] === undefined || this.logins[ip].length == 0) {
- return 0;
- }
-
- this.logins[ip].forEach(function(name) {
+ if(!(ip in this.ip_alias))
+ this.ip_alias = Database.getAliases(ip);
+ this.ip_alias[ip].forEach(function(name) {
names.push(name);
});
@@ -339,16 +334,6 @@ Channel.prototype.getIPRank = function(ip) {
return rank;
}
-Channel.prototype.seen = function(ip, name) {
- name = name.toLowerCase();
- for(var i = 0; i < this.logins[ip].length; i++) {
- if(this.logins[ip][i].toLowerCase() == name) {
- return true;
- }
- }
- return false;
-}
-
Channel.prototype.cacheMedia = function(media) {
// Prevent the copy in the playlist from messing with this one
media = media.dup();
@@ -362,7 +347,7 @@ Channel.prototype.cacheMedia = function(media) {
return false;
}
-Channel.prototype.banName = function(actor, name) {
+Channel.prototype.tryNameBan = function(actor, name) {
if(!this.hasPermission(actor, "ban")) {
return false;
}
@@ -370,11 +355,6 @@ Channel.prototype.banName = function(actor, name) {
name = name.toLowerCase();
var rank = this.getRank(name);
- if(rank < 1) {
- actor.socket.emit("errorMsg", {msg: "You can't ban guest names. Use a kick or IP ban."});
- return false;
- }
-
if(rank >= actor.rank) {
actor.socket.emit("errorMsg", {msg: "You don't have permission to ban this person."});
return false;
@@ -409,47 +389,47 @@ Channel.prototype.unbanName = function(actor, name) {
return Database.channelUnbanName(this.name, name);
}
-Channel.prototype.tryIPBan = function(actor, data) {
+Channel.prototype.tryIPBan = function(actor, name, range) {
if(!this.hasPermission(actor, "ban")) {
return false;
}
- if(typeof data.id != "string") {
- return false;
- }
- if(typeof data.name != "string") {
- return false;
- }
- var ip = this.hideIP(data.id);
- if(this.getIPRank(ip) >= actor.rank) {
- actor.socket.emit("errorMsg", {msg: "You don't have permission to ban this IP"});
+ if(typeof name != "string") {
return false;
}
+ var ips = Database.ipForName(name);
+ var chan = this;
+ ips.forEach(function(ip) {
+ if(chan.getIPRank(ip) >= actor.rank) {
+ actor.socket.emit("errorMsg", {msg: "You don't have permission to ban IP: x.x." + ip.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1")});
+ return false;
+ }
- if(data.range) {
- ip = ip.replace(/(\d+)\.(\d+)\.(\d+)\.(\d+)/, "$1.$2.$3");
- for(var ip2 in this.logins) {
- if(ip2.indexOf(ip) == 0 && this.getIPRank(ip2) >= actor.rank) {
- actor.socket.emit("errorMsg", {msg: "You don't have permission to ban this IP"});
- return false;
+ if(range) {
+ ip = ip.replace(/(\d+)\.(\d+)\.(\d+)\.(\d+)/, "$1.$2.$3");
+ for(var ip2 in chan.ip_alias) {
+ if(ip2.indexOf(ip) == 0 && chan.getIPRank(ip2) >= actor.rank) {
+ actor.socket.emit("errorMsg", {msg: "You don't have permission to ban IP: x.x." + ip2.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1")});
+ return false;
+ }
}
}
- }
- this.ipbans[ip] = [data.name, actor.name];
- this.broadcastBanlist();
- this.logger.log(ip + " (" + data.name + ") was banned by " + actor.name);
+ chan.ipbans[ip] = [name, actor.name];
+ chan.broadcastBanlist();
+ chan.logger.log(ip + " (" + name + ") was banned by " + actor.name);
- for(var i = 0; i < this.users.length; i++) {
- if(this.users[i].ip.indexOf(ip) == 0) {
- this.kick(this.users[i], "Your IP is banned!");
- i--;
+ for(var i = 0; i < chan.users.length; i++) {
+ if(chan.users[i].ip.indexOf(ip) == 0) {
+ chan.kick(chan.users[i], "Your IP is banned!");
+ i--;
+ }
}
- }
- if(!this.registered)
- return false;
+ if(!chan.registered)
+ return false;
- // Update database ban table
- return Database.channelBan(this.name, ip, data.name, actor.name);
+ // Update database ban table
+ return Database.channelBan(chan.name, ip, name, actor.name);
+ });
}
Channel.prototype.banIP = function(actor, receiver) {
@@ -488,8 +468,8 @@ Channel.prototype.unbanIP = function(actor, ip) {
}
Channel.prototype.tryUnban = function(actor, data) {
- if(data.id) {
- var ip = this.hideIP(data.id);
+ if(data.ip_hidden) {
+ var ip = this.hideIP(data.ip_hidden);
this.unbanIP(actor, ip);
}
else if(data.name) {
@@ -531,9 +511,6 @@ Channel.prototype.search = function(query, callback) {
/* REGION User interaction */
Channel.prototype.userJoin = function(user) {
- if(!(user.ip in this.logins)) {
- this.logins[user.ip] = [];
- }
var parts = user.ip.split(".");
var slash24 = parts[0] + "." + parts[1] + "." + parts[2];
// GTFO
@@ -653,27 +630,29 @@ Channel.prototype.hideIP = function(ip) {
return chars.join("");
}
+Channel.prototype.sendLoginHistory = function(user) {
+ if(user.rank < 2)
+ return;
+
+ user.socket.emit("recentLogins", this.login_hist);
+}
+
Channel.prototype.sendRankStuff = function(user) {
if(this.hasPermission(user, "ban")) {
var ents = [];
for(var ip in this.ipbans) {
if(this.ipbans[ip] != null) {
- var name;
- if(ip in this.logins) {
- name = this.logins[ip].join(", ");
- }
- else {
- name = this.ipbans[ip][0];
- }
- var id = this.hideIP(ip);
+ var name = this.ipbans[ip][0];
+ var ip_hidden = this.hideIP(ip);
var disp = ip;
if(user.rank < Rank.Siteadmin) {
- disp = "(Hidden)";
+ disp = "x.x." + ip.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1");
}
ents.push({
- ip: disp,
- id: id,
+ ip_displayed: disp,
+ ip_hidden: ip_hidden,
name: name,
+ aliases: this.ip_alias[ip] || [],
banner: this.ipbans[ip][1]
});
}
@@ -681,17 +660,20 @@ Channel.prototype.sendRankStuff = function(user) {
for(var name in this.namebans) {
if(this.namebans[name] != null) {
ents.push({
- ip: "*",
+ ip_displayed: "*",
+ ip_hidden: false,
name: name,
+ aliases: this.name_alias[name] || [],
banner: this.namebans[name]
});
}
}
- user.socket.emit("banlist", {entries: ents});
+ user.socket.emit("banlist", ents);
}
+ // TODO get rid of this
if(Rank.hasPermission(user, "seenlogins")) {
var ents = [];
- for(var ip in this.logins) {
+ for(var ip in this.ip_alias) {
var disp = ip;
if(user.rank < Rank.Siteadmin) {
disp = "(Hidden)";
@@ -702,7 +684,7 @@ Channel.prototype.sendRankStuff = function(user) {
ents.push({
ip: disp,
id: this.hideIP(ip),
- names: this.logins[ip],
+ names: this.ip_alias[ip],
banned: banned
});
}
@@ -797,9 +779,21 @@ Channel.prototype.broadcastUsercount = function() {
}
Channel.prototype.broadcastNewUser = function(user) {
- if(!this.seen(user.ip, user.name)) {
- this.logins[user.ip].push(user.name);
- }
+ var aliases = Database.getAliases(user.ip);
+ var chan = this;
+ this.ip_alias[user.ip] = aliases;
+ aliases.forEach(function(alias) {
+ chan.name_alias[alias] = aliases;
+ });
+
+ this.login_hist.unshift({
+ name: user.name,
+ aliases: this.ip_alias[user.ip],
+ time: Date.now()
+ });
+ if(this.login_hist.length > 20)
+ this.login_hist.pop();
+
if(user.name.toLowerCase() in this.namebans &&
this.namebans[user.name.toLowerCase()] != null) {
this.kick(user, "You're banned!");
@@ -850,24 +844,20 @@ Channel.prototype.broadcastBanlist = function() {
var adminents = [];
for(var ip in this.ipbans) {
if(this.ipbans[ip] != null) {
- var name;
- if(ip in this.logins) {
- name = this.logins[ip].join(", ");
- }
- else {
- name = this.ipbans[ip][0];
- }
- var id = this.hideIP(ip);
+ var name = this.ipbans[ip][0];
+ var ip_hidden = this.hideIP(ip);
ents.push({
- ip: "(Hidden)",
- id: id,
+ ip_displayed: "x.x." + ip.replace(/\d+\.\d+\.(\d+\.\d+)/, "$1"),
+ ip_hidden: ip_hidden,
name: name,
+ aliases: this.ip_alias[ip] || [],
banner: this.ipbans[ip][1]
});
adminents.push({
- ip: ip,
- id: id,
+ ip_displayed: ip,
+ ip_hidden: ip_hidden,
name: name,
+ aliases: this.ip_alias[ip] || [],
banner: this.ipbans[ip][1]
});
}
@@ -875,13 +865,17 @@ Channel.prototype.broadcastBanlist = function() {
for(var name in this.namebans) {
if(this.namebans[name] != null) {
ents.push({
- ip: "*",
+ ip_displayed: "*",
+ ip_hidden: false,
name: name,
+ aliases: this.name_alias[name] || [],
banner: this.namebans[name]
});
adminents.push({
- ip: "*",
+ ip_displayed: "*",
+ ip_hidden: false,
name: name,
+ aliases: this.name_alias[name] || [],
banner: this.namebans[name]
});
}
@@ -889,10 +883,10 @@ Channel.prototype.broadcastBanlist = function() {
for(var i = 0; i < this.users.length; i++) {
if(this.hasPermission(this.users[i], "ban")) {
if(this.users[i].rank >= Rank.Siteadmin) {
- this.users[i].socket.emit("banlist", {entries: adminents});
+ this.users[i].socket.emit("banlist", adminents);
}
else {
- this.users[i].socket.emit("banlist", {entries: ents});
+ this.users[i].socket.emit("banlist", ents);
}
}
}
diff --git a/chatcommand.js b/chatcommand.js
index a134574a..89d77f73 100644
--- a/chatcommand.js
+++ b/chatcommand.js
@@ -39,7 +39,7 @@ function handle(chan, user, msg, data) {
handleBan(chan, user, msg.substring(5).split(" "));
}
else if(msg.indexOf("/ipban ") == 0) {
- handleIPBan(chan, user, msg.substring(5).split(" "));
+ handleIPBan(chan, user, msg.substring(7).split(" "));
}
else if(msg.indexOf("/unban ") == 0) {
handleUnban(chan, user, msg.substring(7).split(" "));
@@ -77,30 +77,13 @@ function handleKick(chan, user, args) {
}
function handleIPBan(chan, user, args) {
- if(chan.hasPermission(user, "ban") && args.length > 0) {
- args[0] = args[0].toLowerCase();
- var kickee;
- for(var i = 0; i < chan.users.length; i++) {
- if(chan.users[i].name.toLowerCase() == args[0]) {
- kickee = chan.users[i];
- break;
- }
- }
- if(kickee && kickee.rank < user.rank) {
- chan.logger.log("*** " + user.name + " banned " + args[0]);
- args[0] = "";
- var reason = args.join(" ");
- chan.kick(kickee, "(banned) " + reason);
- chan.banIP(user, kickee);
- }
- }
+ chan.tryIPBan(user, args[0], args[1]);
+ // Ban the name too for good measure
+ chan.tryNameBan(user, args[0]);
}
function handleBan(chan, user, args) {
- if(chan.hasPermission(user, "ban") && args.length > 0) {
- args[0] = args[0].toLowerCase();
- chan.banName(user, args[0]);
- }
+ chan.tryNameBan(user, args[0]);
}
function handleUnban(chan, user, args) {
diff --git a/database.js b/database.js
index 13f5196f..d6a4d095 100644
--- a/database.js
+++ b/database.js
@@ -918,7 +918,6 @@ function recordVisit(ip, name) {
var results = db.querySync(query);
if(!results) {
- console.log(results);
Logger.errlog.log("! Failed to record visit");
}
@@ -931,11 +930,60 @@ function recordVisit(ip, name) {
");"].join(""),
[ip]
));
- console.log(results);
return results;
}
+function getAliases(ip) {
+ var db = getConnection();
+ if(!db) {
+ return [];
+ }
+
+ var query = createQuery(
+ "SELECT name FROM aliases WHERE ip=?",
+ [ip]
+ );
+
+ var results = db.querySync(query);
+ if(!results) {
+ Logger.errlog.log("! Failed to retrieve aliases");
+ return [];
+ }
+
+ var names = [];
+ results.fetchAllSync().forEach(function(row) {
+ names.push(row.name);
+ });
+
+ return names;
+}
+
+function ipForName(name) {
+ var db = getConnection();
+ if(!db) {
+ return [];
+ }
+
+ var query = createQuery(
+ "SELECT ip FROM aliases WHERE name=?",
+ [name]
+ );
+
+ var results = db.querySync(query);
+ if(!results) {
+ Logger.errlog.log("! Failed to retrieve IP for name");
+ return [];
+ }
+
+ var ips = [];
+ results.fetchAllSync().forEach(function(row) {
+ ips.push(row.ip);
+ });
+
+ return ips;
+}
+
exports.setup = setup;
exports.getConnection = getConnection;
exports.createQuery = createQuery;
@@ -966,3 +1014,5 @@ exports.loadUserPlaylist = loadUserPlaylist;
exports.saveUserPlaylist = saveUserPlaylist;
exports.deleteUserPlaylist = deleteUserPlaylist;
exports.recordVisit = recordVisit;
+exports.getAliases = getAliases;
+exports.ipForName = ipForName;
diff --git a/user.js b/user.js
index 1b461751..22372bf9 100644
--- a/user.js
+++ b/user.js
@@ -379,6 +379,12 @@ User.prototype.initCallbacks = function() {
}
}.bind(this));
+ this.socket.on("requestLoginHistory", function() {
+ if(this.channel != null) {
+ this.channel.sendLoginHistory(this);
+ }
+ }.bind(this));
+
this.socket.on("requestAcl", function() {
if(this.channel != null) {
this.channel.sendACL(this);
diff --git a/www/assets/js/callbacks.js b/www/assets/js/callbacks.js
index 4369933a..aeec406e 100644
--- a/www/assets/js/callbacks.js
+++ b/www/assets/js/callbacks.js
@@ -250,26 +250,60 @@ Callbacks = {
banlist: function(entries) {
var tbl = $("#banlist table");
+ // dumb hack because of jquery UI
+ // sortable turns tables and lists into a mess of race conditions
+ if(!tbl.hasClass("table")) {
+ setTimeout(function() {
+ Callbacks.banlist(entries);
+ }, 100);
+ return;
+ }
if(tbl.children().length > 1) {
$(tbl.children()[1]).remove();
}
for(var i = 0; i < entries.length; i++) {
- var tr = $("
").appendTo(tbl);
+ var tr = document.createElement("tr");
var remove = $("").addClass("btn btn-mini btn-danger")
.appendTo($(" | ").appendTo(tr));
$("").addClass("icon-remove-circle").appendTo(remove);
- var ip = $(" | ").text(entries[i].ip).appendTo(tr);
+ var ip = $(" | ").text(entries[i].ip_displayed).appendTo(tr);
var name = $(" | ").text(entries[i].name).appendTo(tr);
- var aliases = $(" | ").text(entries[i].aliases).appendTo(tr);
+ var aliases = $(" | ").text(entries[i].aliases.join(", ")).appendTo(tr);
var banner = $(" | ").text(entries[i].banner).appendTo(tr);
- var callback = (function(id, name) { return function() {
+ var callback = (function(ip, name) { return function() {
socket.emit("unban", {
- id: id,
+ ip: ip,
name: name
});
- } })(entries[i].id, entries[i].name);
+ } })(entries[i].ip_hidden, entries[i].name);
remove.click(callback);
+
+ $(tr).appendTo(tbl);
+ }
+ },
+
+ recentLogins: function(entries) {
+ var tbl = $("#loginhistory table");
+ // dumb hack because of jquery UI
+ // sortable turns tables and lists into a mess of race conditions
+ if(!tbl.hasClass("table")) {
+ setTimeout(function() {
+ Callbacks.recentLogins(entries);
+ }, 100);
+ return;
+ }
+ if(tbl.children().length > 1) {
+ $(tbl.children()[1]).remove();
+ }
+ for(var i = 0; i < entries.length; i++) {
+ var tr = document.createElement("tr");
+ var name = $(" | ").text(entries[i].name).appendTo(tr);
+ var aliases = $(" | ").text(entries[i].aliases.join(", ")).appendTo(tr);
+ var time = new Date(entries[i].time).toTimeString();
+ $(" | ").text(time).appendTo(tr);
+
+ $(tr).appendTo(tbl);
}
},
diff --git a/www/assets/js/channelsettings.js b/www/assets/js/channelsettings.js
index 566cf59a..7c0f507c 100644
--- a/www/assets/js/channelsettings.js
+++ b/www/assets/js/channelsettings.js
@@ -20,6 +20,11 @@
clickHandler("#show_filteredit", "#filteredit");
clickHandler("#show_cssedit", "#cssedit");
clickHandler("#show_jsedit", "#jsedit");
+ clickHandler("#show_banlist", "#banlist");
+ clickHandler("#show_loginhistory", "#loginhistory");
+ $("#show_loginhistory").click(function() {
+ socket.emit("requestLoginHistory");
+ });
genPermissionsEditor();
diff --git a/www/channeloptions.html b/www/channeloptions.html
index 00cc61a2..3ed3d382 100644
--- a/www/channeloptions.html
+++ b/www/channeloptions.html
@@ -12,7 +12,7 @@
CSS Editor
JS Editor
Ban List
- Recent Connections
+ Recent Connections
@@ -102,3 +102,27 @@ Filters Here
+
+
+
+
+ | Unban |
+ IP |
+ Name |
+ Aliases |
+ Banned By |
+
+
+
+
+
+
+
+
+ | Name |
+ Aliases |
+ Time |
+
+
+
+