From fe00fb8c83fad0f36a86ab2c288ec3ffbccf5938 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Fri, 13 Dec 2013 18:52:13 -0600 Subject: [PATCH] Refuctor channel related db functions --- lib/channel.js | 62 ++--- lib/database.js | 501 +-------------------------------------- lib/database/channels.js | 292 ++++++++++++++++++++++- 3 files changed, 320 insertions(+), 535 deletions(-) diff --git a/lib/channel.js b/lib/channel.js index 89e2baa6..b0e9287b 100644 --- a/lib/channel.js +++ b/lib/channel.js @@ -25,6 +25,7 @@ var $util = require("./utilities"); var AsyncQueue = require("./asyncqueue"); var ActionLog = require("./actionlog"); var InfoGetter = require("./get-info"); +var db = require("./database"); var Channel = function(name) { var self = this; @@ -134,10 +135,10 @@ var Channel = function(name) { self.ipkey += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"[parseInt(Math.random() * 65)] } - Server.getServer().db.loadChannelData(self, function (err) { - if (err && err === "channel_dead") + db.channels.load(self, function (err) { + if (err) return; - else if (!err || err === "channel_unregistered") + else if (!err || err === "Channel is not registered") self.dbloaded = true; self.tryLoadDump(); @@ -411,8 +412,8 @@ Channel.prototype.tryRegister = function (user) { }); } else { - self.server.db.registerChannel(self.name, user.name, - function (err, res) { + db.channels.register(self.name, user.name, + function (err, res) { if(err) { user.socket.emit("registerChannel", { success: false, @@ -455,7 +456,7 @@ Channel.prototype.unregister = function (user) { }); return; } - self.server.db.dropChannel(self.name, function (err, res) { + db.channels.drop(self.name, function (err, res) { if(err) { user.socket.emit("unregisterChannel", { success: false, @@ -472,7 +473,7 @@ Channel.prototype.unregister = function (user) { Channel.prototype.getRank = function (name, callback) { var self = this; - self.server.db.getGlobalRank(name, function (err, global) { + db.users.getGlobalRank(name, function (err, global) { if (self.dead) return; @@ -486,8 +487,8 @@ Channel.prototype.getRank = function (name, callback) { return; } - self.server.db.getChannelRank(self.name, name, - function (err, rank) { + db.channels.getRank(self.name, name, + function (err, rank) { if(err) { callback(err, null); return; @@ -503,13 +504,13 @@ Channel.prototype.saveRank = function (user, callback) { return; if(!user.saverank) return; - this.server.db.setChannelRank(this.name, user.name, user.rank, callback); + db.channels.setRank(this.name, user.name, user.rank, callback); } Channel.prototype.saveInitialRank = function (user, callback) { if(!this.registered) return; - this.server.db.insertChannelRank(this.name, user.name, user.rank, callback); + db.channels.newRank(this.name, user.name, user.rank, callback); }; Channel.prototype.getIPRank = function (ip, callback) { @@ -517,7 +518,7 @@ Channel.prototype.getIPRank = function (ip, callback) { self.server.db.listAliases(ip, function (err, names) { if (self.dead) return; - self.server.db.listGlobalRanks(names, function (err, res) { + db.users.getGlobalRanks(names, function (err, res) { if(err) { callback(err, null); return; @@ -533,8 +534,8 @@ Channel.prototype.getIPRank = function (ip, callback) { return; } - self.server.db.listChannelUserRanks(self.name, names, - function (err, res) { + db.channels.getRanks(self.name, names, + function (err, res) { if (self.dead) return; @@ -564,7 +565,7 @@ Channel.prototype.cacheMedia = function(media) { return; } if(self.registered) { - self.server.db.addToLibrary(self.name, media); + db.channels.addToLibrary(self.name, media); } } @@ -627,7 +628,7 @@ Channel.prototype.tryNameBan = function(actor, name) { return; } - self.server.db.addChannelBan(self.name, "*", name, actor.name); + db.channels.ban(self.name, "*", name, "", actor.name); }); } @@ -644,7 +645,7 @@ Channel.prototype.unbanName = function(actor, name) { if (!self.registered) return; - self.server.db.clearChannelNameBan(self.name, name, function (err, res) { + db.channels.unbanName(self.name, name, function (err, res) { if (self.dead) return; @@ -669,7 +670,7 @@ Channel.prototype.tryIPBan = function(actor, name, range) { }); return; } - self.server.db.listIPsForName(name, function (err, ips) { + db.listIPsForName(name, function (err, ips) { if (self.dead) return; @@ -715,9 +716,8 @@ Channel.prototype.tryIPBan = function(actor, name, range) { if(!self.registered) return; - self.server.db.addChannelBan(self.name, ip, name, - actor.name, - function (err, res) { + db.channels.ban(self.name, ip, name, "", actor.name, + function (err, res) { if (self.dead) return; @@ -761,7 +761,7 @@ Channel.prototype.unbanIP = function(actor, ip) { return false; // Update database ban table - self.server.db.clearChannelIPBan(self.name, ip); + db.channels.unbanIP(self.name, ip); } Channel.prototype.tryUnban = function(actor, data) { @@ -780,7 +780,7 @@ Channel.prototype.search = function(query, callback) { callback([]); return; } - self.server.db.searchLibrary(self.name, query, function (err, res) { + db.channels.searchLibrary(self.name, query, function (err, res) { if(err) { res = []; } @@ -1032,7 +1032,7 @@ Channel.prototype.sendChatFilters = function(user) { Channel.prototype.sendChannelRanks = function(user) { if(user.rank >= 3 && this.registered) { - this.server.db.listChannelRanks(this.name, function (err, res) { + db.channels.allRanks(this.name, function (err, res) { if(err) { user.socket.emit("errorMsg", { msg: "Internal error: " + err @@ -1139,7 +1139,7 @@ Channel.prototype.broadcastNewUser = function(user) { user.socket.emit("channelNotRegistered"); user.socket.emit("rank", user.rank); } - self.server.db.listAliases(user.ip, function (err, aliases) { + db.listAliases(user.ip, function (err, aliases) { if (self.dead) return; @@ -1605,8 +1605,8 @@ Channel.prototype.addMedia = function(data, user) { self.plqueue.queue(function (q) { if (self.dead) return; - self.server.db.getLibraryItem(self.name, data.id, - function (err, item) { + db.getLibraryItem(self.name, data.id, + function (err, item) { if (self.dead) return; @@ -1765,8 +1765,8 @@ Channel.prototype.tryUncache = function(user, data) { } if (!self.registered) return; - self.server.db.removeFromLibrary(self.name, data.id, - function (err, res) { + db.channels.deleteFromLibrary(self.name, data.id, + function (err, res) { if (self.dead) return; @@ -2507,8 +2507,8 @@ Channel.prototype.trySetRank = function(user, data) { return; if(rrank >= user.rank) return; - self.server.db.setChannelRank(self.name, data.user, - data.rank, function (err, res) { + db.channels.setRank(self.name, data.user, + data.rank, function (err, res) { if (self.dead) return; diff --git a/lib/database.js b/lib/database.js index 3ab70b4a..bb417721 100644 --- a/lib/database.js +++ b/lib/database.js @@ -32,6 +32,8 @@ module.exports.init = function (cfg) { global_ipbans = {}; module.exports.users = require("./database/accounts"); module.exports.users.init(); + module.exports.channels = require("./database/channels"); + module.exports.channels.init(); }; module.exports.query = function (query, sub, callback) { @@ -77,42 +79,6 @@ function blackHole() { module.exports.oldinit = function () { var query; - // Create channel table - query = ["CREATE TABLE IF NOT EXISTS `channels` (", - "`id` INT NOT NULL AUTO_INCREMENT,", - "`name` VARCHAR(255) NOT NULL,", - "`owner` VARCHAR(20) NOT NULL,", - "PRIMARY KEY(`id`))", - "ENGINE = MyISAM ", - "CHARACTER SET utf8;"].join(""); - - module.exports.query(query, function (err, res) { - if(err) { - Logger.errlog.log("! Failed to create channels table"); - } - }); - - // Create registration table - query = ["CREATE TABLE IF NOT EXISTS `registrations` (", - "`id` INT NOT NULL AUTO_INCREMENT,", - "`uname` VARCHAR(20) NOT NULL,", - "`pw` VARCHAR(64) NOT NULL,", - "`global_rank` INT NOT NULL,", - "`session_hash` VARCHAR(64) NOT NULL,", - "`expire` BIGINT NOT NULL,", - "`profile_image` VARCHAR(255) NOT NULL,", - "`profile_text` TEXT NOT NULL,", - "`email` VARCHAR(255) NOT NULL,", - "PRIMARY KEY (`id`))", - "ENGINE = MyISAM ", - "CHARACTER SET utf8;"].join(""); - - module.exports.query(query, function (err, res) { - if(err) { - Logger.errlog.log("! Failed to create registration table"); - } - }); - // Create global bans table query = ["CREATE TABLE IF NOT EXISTS `global_bans` (", "`ip` VARCHAR(15) NOT NULL,", @@ -285,470 +251,7 @@ module.exports.clearGlobalIPBan = function (ip, callback) { /* END REGION */ -/* REGION channels */ - -module.exports.loadChannelData = function (chan, callback) { - if(typeof callback !== "function") - callback = blackHole; - - if(!$util.isValidChannelName(chan.name)) { - callback("Invalid channel name", null); - return; - } - - var query = "SELECT * FROM channels WHERE name=?"; - - module.exports.query(query, [chan.name], function (err, res) { - if(err) { - callback(err, null); - return; - } - - if(res.length == 0) { - callback("channel_unregistered", null); - return; - } - - if (chan.dead) { - callback("channel_dead", null); - return; - } - - if(res[0].name != chan.name) - chan.name = res[0].name; - chan.registered = true; - - // Load bans - query = "SELECT * FROM `chan_" + chan.name + "_bans`"; - module.exports.query(query, function (err, res) { - if (chan.dead) { - callback("channel_dead", null); - return; - } - - if(err) { - callback(err, null); - return; - } - - for(var i in res) { - var r = res[i]; - if(r.ip === "*") - chan.namebans[r.name] = r.banner; - else - chan.ipbans[r.ip] = [r.name, r.banner]; - } - - chan.logger.log("*** Loaded channel from database"); - callback(null, true); - }); - }); -}; - -module.exports.setChannelRank = function (channame, name, rank, callback) { - if(typeof callback !== "function") - callback = blackHole; - - if(!$util.isValidChannelName(channame)) { - callback("Invalid channel name", null); - return; - } - - var query = "INSERT INTO `chan_" + channame + "_ranks` " + - "(name, rank) VALUES (?, ?) " + - "ON DUPLICATE KEY UPDATE rank=?"; - - module.exports.query(query, [name, rank, rank], callback); -}; - -module.exports.insertChannelRank = function (channame, name, rank, callback) { - if(typeof callback !== "function") - callback = blackHole; - - if(!$util.isValidChannelName(channame)) { - callback("Invalid channel name", null); - return; - } - - var query = "INSERT INTO `chan_" + channame + "_ranks` " + - "(name, rank) VALUES (?, ?) " + - "ON DUPLICATE KEY UPDATE rank=rank"; - - module.exports.query(query, [name, rank], callback); -}; - -module.exports.listChannelRanks = function (channame, callback) { - if(typeof callback !== "function") - return; - - if(!$util.isValidChannelName(channame)) { - callback("Invalid channel name", null); - return; - } - - var query = "SELECT * FROM `chan_" + channame + "_ranks` WHERE 1"; - module.exports.query(query, callback); -}; - -module.exports.addToLibrary = function (channame, media, callback) { - if(typeof callback !== "function") - callback = blackHole; - - if(!$util.isValidChannelName(channame)) { - callback("Invalid channel name"); - return; - } - - var query = "INSERT INTO `chan_" + channame + "_library` " + - "(id, title, seconds, type) " + - "VALUES (?, ?, ?, ?) " + - "ON DUPLICATE KEY UPDATE id=id"; - var params = [ - media.id, - media.title, - media.seconds, - media.type - ]; - module.exports.query(query, params, callback); -}; - -module.exports.removeFromLibrary = function (channame, id, callback) { - if(typeof callback !== "function") - callback = blackHole; - - if(!$util.isValidChannelName(channame)) { - callback("Invalid channel name", null); - return; - } - - var query = "DELETE FROM `chan_" + channame + "_library` WHERE id=?"; - module.exports.query(query, [id], callback); -}; - -module.exports.getLibraryItem = function (channame, id, callback) { - if(typeof callback !== "function") - callback = blackHole; - - if(!$util.isValidChannelName(channame)) { - callback("Invalid channel name", null); - return; - } - - var m = id.match(/([\w-\/\.:]+)/); - if (m) { - id = m[1]; - } else { - callback("Invalid ID", null); - return; - } - - var query = "SELECT id, title, seconds, type FROM " + - "`chan_" + channame + "_library` WHERE id=?"; - - module.exports.query(query, [id], function (err, res) { - if(err) { - callback(err, null); - return; - } - - callback(null, res.length > 0 ? res[0] : null); - }); -}; - -module.exports.searchLibrary = function (channame, term, callback) { - if(typeof callback !== "function") - return; - - if(!$util.isValidChannelName(channame)) { - callback("Invalid channel name", null); - return; - } - - var query = "SELECT id, title, seconds, type FROM " + - "`chan_" + channame + "_library` WHERE title LIKE ?"; - - module.exports.query(query, ["%" + term + "%"], callback); -}; - -module.exports.addChannelBan = function (channame, ip, name, banBy, - callback) { - if(typeof callback !== "function") - callback = blackHole; - - if(!$util.isValidChannelName(channame)) { - callback("Invalid channel name", null); - return; - } - - var query = "INSERT INTO `chan_" + channame + "_bans`" + - "(ip, name, banner) VALUES (?, ?, ?) " + - "ON DUPLICATE KEY UPDATE ip=ip"; - - module.exports.query(query, [ip, name, banBy], callback); -}; - -module.exports.clearChannelIPBan = function (channame, ip, callback) { - if(typeof callback !== "function") - callback = blackHole; - - if(!$util.isValidChannelName(channame)) { - callback("Invalid channel name", null); - return; - } - - var query = "DELETE FROM `chan_" + channame + "_bans` WHERE ip=?"; - module.exports.query(query, [ip], callback); -}; - -module.exports.clearChannelNameBan = function (channame, name, - callback) { - if(typeof callback !== "function") { - callback = blackHole; - return; - } - - var query = "DELETE FROM `chan_" + channame + "_bans` WHERE ip='*'" + - "AND name=?"; - - module.exports.query(query, [name], callback); -}; - -/* END REGION */ - /* REGION Auth */ - -module.exports.isUsernameTaken = function (name, callback) { - if(typeof callback !== "function") - return; - - var query = "SELECT id FROM registrations WHERE uname=?"; - module.exports.query(query, [name], function (err, res) { - if(err) { - callback(err, null); - return; - } - - callback(null, res.length > 0); - }); -}; - -var regInProgress = {}; -module.exports.registerUser = function (name, pw, callback) { - if(typeof callback !== "function") - callback = blackHole; - - if(!$util.isValidUserName(name)) { - callback("Invalid username", null); - return; - } - - if (regInProgress[name]) { - callback("Registration is already in progress", null); - return; - } - - regInProgress[name] = true; - - var postRegister = function (err, res) { - if(err) { - delete regInProgress[name]; - callback(err, null); - return; - } - - createLoginSession(name, function (err, hash) { - if(err) { - delete regInProgress[name]; - // Don't confuse people into thinking the registration - // failed when it was the session that failed - callback(null, ""); - return; - } - - delete regInProgress[name]; - callback(null, hash); - }); - }; - - isUsernameTaken(name, function (err, taken) { - if(err) { - delete regInProgress[name]; - callback(err, null); - return; - } - - if(taken) { - delete regInProgress[name]; - callback("Username already taken", null); - return; - } - - bcrypt.hash(pw, 10, function (err, hash) { - if(err) { - delete regInProgress[name]; - callback(err, null); - return; - } - - var query = "INSERT INTO registrations VALUES " + - "(NULL, ?, ?, 1, '', 0, '', '', '')"; - - module.exports.query(query, [name, hash], postRegister); - }); - }); -}; - -module.exports.userLogin = function (name, pw, session, callback) { - if(typeof callback !== "function") - callback = blackHole; - - var postLogin = function (err, row) { - if(err) { - callback(err, null); - return; - } - - if(row.session_hash) { - callback(null, row); - return; - } - - createLoginSession(name, function (err, hash) { - if(err) { - callback(err, null); - return; - } - - row.session_hash = hash; - callback(null, row); - }); - }; - - if(session) { - module.exports.userLoginSession(name, session, postLogin); - } else if(pw) { - module.exports.userLoginPassword(name, pw, postLogin); - } else { - callback("Invalid login", null); - } -}; - -module.exports.userLoginPassword = function (name, pw, callback) { - if(typeof callback !== "function") - callback = blackHole; - - var query = "SELECT * FROM registrations WHERE uname=?"; - module.exports.query(query, [name], function (err, res) { - if(err) { - callback(err, null); - return; - } - - if(res.length == 0) { - callback("User does not exist", null); - return; - } - - var row = res[0]; - - bcrypt.compare(pw, row.pw, function (err, valid) { - if(valid) { - // For security, erase the password field before returning - delete row["pw"]; - row.session_hash = ""; - callback(null, row); - return; - } - - // Possibly could be a SHA256 hash from an *ancient* version - // of CyTube - - var sha = hashlib.sha256(pw); - if(sha == row.pw) { - // Replace it - bcrypt.hash(pw, 10, function (err, hash) { - if(!err) { - module.exports.query("UPDATE registrations SET pw=? "+ - "WHERE uname=?", [hash, name]); - } - }); - - // Remove password field before returning - delete row["pw"]; - row.session_hash = ""; - callback(null, row); - } else { - callback("Invalid username/password combination", null); - } - }); - }); -}; - -module.exports.userLoginSession = function (name, session, callback) { - if(typeof callback !== "function") - callback = blackHole; - - var query = "SELECT * FROM registrations WHERE uname=? AND " + - "session_hash=?"; - - module.exports.query(query, [name, session], function (err, res) { - if(err) { - callback(err, null); - return; - } - - if(res.length == 0) { - callback("Session expired", null); - return; - } - - var row = res[0]; - - if(row.expire < Date.now()) { - callback("Session expired", null); - return; - } - - callback(null, row); - }); -}; - -module.exports.createLoginSession = function (name, callback) { - if(typeof callback !== "function") - callback = blackHole; - - var salt = $util.randomSalt(32); - var hash = hashlib.sha256(salt + name); - - var query = "UPDATE registrations SET session_hash=?, expire=? " + - "WHERE uname=?"; - - module.exports.query(query, [hash, Date.now() + 604800000, name], - function (err, res) { - if(err) { - callback(err, null); - return; - } - - callback(null, hash); - }); -}; - -module.exports.setUserPassword = function (name, pw, callback) { - if(typeof callback !== "function") - callback = blackHole; - - bcrypt.hash(pw, 10, function (err, hash) { - if(err) { - callback(err, null); - return; - } - - var query = "UPDATE registrations SET pw=? WHERE uname=?"; - module.exports.query(query, [hash, name], callback); - }); -}; - module.exports.getGlobalRank = function (name, callback) { if(typeof callback !== "function") return; diff --git a/lib/database/channels.js b/lib/database/channels.js index 59582898..840ed72a 100644 --- a/lib/database/channels.js +++ b/lib/database/channels.js @@ -1,5 +1,5 @@ var db = require("../database"); -var $util = require("../utilities"); +var valid = require("../utilities").isValidChannelName; var blackHole = function () { }; @@ -19,7 +19,7 @@ function createLibraryTable(name, callback) { db.query("CREATE TABLE `chan_" + name + "_library` (" + "`id` VARCHAR(255) NOT NULL," + "`title` VARCHAR(255) NOT NULL," + - "`seconds` INT NOT NULL," + "`seconds` INT NOT NULL," + "`type` VARCHAR(2) NOT NULL," + "PRIMARY KEY (`id`))" + "CHARACTER SET utf8", callback); @@ -36,6 +36,10 @@ function createBansTable(name, callback) { } function initTables(name, owner, callback) { + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } createRanksTable(name, function (err) { if (err) { callback(err, null); @@ -58,7 +62,7 @@ function initTables(name, owner, callback) { callback(err, null); return; } - + callback(null, true); }); }); @@ -87,6 +91,11 @@ module.exports = { return; } + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + dbutil.query("SELECT name FROM `channels` WHERE name=?", [name], function (err, rows) { @@ -106,6 +115,11 @@ module.exports = { return; } + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + dbutil.query("SELECT * FROM `channels` WHERE name=?", [name], function (err, rows) { @@ -149,7 +163,7 @@ module.exports = { return; } - if (!$util.isValidChannelName(name)) { + if (!valid(name)) { callback("Invalid channel name. Channel names may consist of 1-30 " + "characters a-z, A-Z, 0-9, -, and _", null); return; @@ -197,6 +211,11 @@ module.exports = { callback = blackHole; } + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + dropTable("chan_" + name + "_ranks", function (err) { dropTable("chan_" + name + "_bans", function (e2) { if (err && e2) { @@ -221,7 +240,7 @@ module.exports = { } callback(err, !Boolean(err)); - } + }); }); }); }); @@ -246,6 +265,68 @@ module.exports = { }); }, + /** + * Loads the channel from the database + */ + load: function (chan, callback) { + if (typeof callback !== "function") { + callback = blackHole; + } + + if (!valid(chan.name)) { + callback("Invalid channel name", null); + return; + } + + db.query("SELECT * FROM `channels` WHERE name=?", chan.name, function (err, res) { + if (err) { + callback(err, null); + return; + } + + if (res.length === 0) { + callback("Channel is not registered", null); + return; + } + + if (chan.dead) { + callback("Channel is dead", null); + return; + } + + // Note that before this line, chan.name might have a different capitalization + // than the database has stored. Update accordingly. + chan.name = res[0].name; + chan.canonical_name = chan.name.toLowerCase(); + chan.registered = true; + + // Load bans + db.query("SELECT * FROM `chan_" + chan.name + "_bans`", function (err, rows) { + if (chan.dead) { + callback("Channel is dead", null); + return; + } + + if (err) { + callback(err, null); + return; + } + + for (var i = 0; i < rows.length; i++) { + var r = rows[i]; + if (r.ip === "*") { + chan.namebans[r.name] = r; + } else { + chan.ipbans[r.ip] = r; + } + } + + chan.logger.log("*** Loaded channel from database"); + callback(null, true); + }); + }); + }, + /** * Looks up a user's rank */ @@ -254,6 +335,11 @@ module.exports = { return; } + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + db.query("SELECT name,rank FROM `chan_" + chan + "_ranks` WHERE name=?", [name], function (err, rows) { @@ -279,6 +365,11 @@ module.exports = { return; } + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + var replace = "(" + names.map(function () { return "?"; }).join(",") + ")"; db.query("SELECT name,rank FROM `chan_" + chan + "_ranks` WHERE name IN " + replace, @@ -291,4 +382,195 @@ module.exports = { callback(null, rows); }); }, + + /** + * Query all user ranks at once + */ + allRanks: function (chan, callback) { + if (typeof callback !== "function") { + return; + } + + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + + db.query("SELECT name,rank FROM `chan_" + chan + "_ranks` WHERE 1", callback); + }, + + /** + * Updates a user's rank + */ + setRank: function (chan, name, rank, callback) { + if (typeof callback !== "function") { + callback = blackHole; + } + + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + + db.query("INSERT INTO `chan_" + chan + "_ranks` (name, rank) VALUES (?, ?) " + + "ON DUPLICATE KEY UPDATE rank=?", [name, rank, rank], callback); + }, + + /** + * Inserts a new user rank entry without clobbering an existing one + */ + newRank: function (chan, name, rank, callback) { + if (typeof callback !== "function") { + callback = blackHole; + } + + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + + db.query("INSERT INTO `chan_" + chan + "_ranks` (name, rank) VALUES (?, ?) " + + "ON DUPLICATE KEY UPDATE rank=rank", [name, rank], callback); + }, + + /** + * Removes a user's rank entry + */ + deleteRank: function (chan, name, callback) { + if (typeof callback !== "function") { + callback = blackHole; + } + + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + + db.query("DELETE FROM `chan_" + chan + "_ranks` WHERE name=?", [name], callback); + }, + + /** + * Adds a media item to the library + */ + addToLibrary: function (chan, media, callback) { + if (typeof callback !== "function") { + callback = blackHole; + } + + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + + db.query("INSERT INTO `chan_" + chan + "_library` (id, title, seconds, type) " + + "VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE id=id", + [media.id, media.title, media.seconds, media.type], callback); + }, + + /** + * Retrieves a media item from the library by id + */ + getLibraryItem: function (chan, id, callback) { + if (typeof callback !== "function") { + return; + } + + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + + db.query("SELECT * FROM `chan_" + chan + "_library` WHERE id=?", [id], + function (err, rows) { + if (err) { + callback(err, null); + return; + } + + if (rows.length === 0) { + callback("Item not in library", null); + } else { + callback(null, rows[0]); + } + }); + }, + + /** + * Search the library by title + */ + searchLibrary: function (chan, search, callback) { + if (typeof callback !== "function") { + return; + } + + db.query("SELECT * FROM `chan_" + chan + "_library` WHERE title LIKE ?", + ["%" + search + "%"], callback); + }, + + /** + * Deletes a media item from the library + */ + deleteFromLibrary: function (chan, id, callback) { + if (typeof callback !== "function") { + callback = blackHole; + } + + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + + db.query("DELETE FROM `chan_" + chan + "_library` WHERE id=?", [id], callback); + }, + + /** + * Add a ban to the banlist + */ + ban: function (chan, ip, name, note, bannedby, callback) { + if (typeof callback !== "function") { + callback = blackHole; + } + + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + + db.query("INSERT INTO `chan_" + chan + "_bans` (ip, name, reason, bannedby) " + + "VALUES (?, ?, ?, ?)", [ip, name, reason, bannedby], callback); + }, + + /** + * Removes a ban from the banlist + */ + unbanName: function (chan, name, callback) { + if (typeof callback !== "function") { + callback = blackHole; + } + + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + + db.query("DELETE FROM `chan_" + chan + "_bans` WHERE ip='*' AND name=?", + [name], callback); + }, + + /** + * Removes a ban from the banlist + */ + unbanIP: function (chan, ip, callback) { + if (typeof callback !== "function") { + callback = blackHole; + } + + if (!valid(chan)) { + callback("Invalid channel name", null); + return; + } + + db.query("DELETE FROM `chan_" + chan + "_bans` WHERE ip=?", + [ip], callback); + } };