using System; using System.Data; using System.Threading; using System.Collections.Generic; using MySql.Data.MySqlClient; using Woodpecker.Core; using Woodpecker.Storage; using Woodpecker.Sessions; using Woodpecker.Net.Game.Messages; using Woodpecker.Game.Users; using Woodpecker.Game.Rooms.Instances; namespace Woodpecker.Game.Moderation { /// /// Provides all kinds of functions for moderation in the virtual hotel, eg, bans etc. /// public class moderationManager { #region Fields private List mChatLogStack; private Thread mChatLogWorker; /// /// True if the logging of in-game chat should be enabled. /// private bool mLogChat; public bool logChat { get { return mLogChat; } set { mLogChat = value; if (mLogChat) { mChatLogStack = new List(); mChatLogWorker = new Thread(new ThreadStart(this.processChatLogStack)); mChatLogWorker.Priority = ThreadPriority.BelowNormal; mChatLogWorker.Start(); Logging.Log("Logging of in-game chat enabled."); } else { if (mChatLogWorker != null) { mChatLogWorker.Abort(); mChatLogStack.Clear(); mChatLogWorker = null; mChatLogStack = null; } Logging.Log("Logging of in-game chat disabled."); } } } #endregion #region Methods #region Bans /// /// Returns true if the given user is (still) banned. If so, then the ban reason is passed through the out Reason string. /// /// The database ID of the user to check. /// A string that should be passed with the out parameter and which will contain the banreason if banned. public bool isBanned(int userID, out string Reason) { Reason = ""; Database Database = new Database(false, true); Database.addParameterWithValue("userid", userID); Database.Open(); if (Database.Ready) { Reason = Database.getString("SELECT reason FROM moderation_bans WHERE userid = @userid AND expires > NOW() LIMIT 1"); } return (Reason.Length > 0); } public bool isBanned(string ipAddress, string machineID, out string Reason) { Reason = ""; Database Database = new Database(false, true); Database.addParameterWithValue("ip", ipAddress); Database.addParameterWithValue("machineid", machineID); Database.Open(); if (Database.Ready) { Reason = Database.getString("SELECT reason FROM moderation_bans WHERE ip = @ip OR machineid = @machineid AND expires > NOW() LIMIT 1"); } return (Reason.Length > 0); } /// /// Removes all expired bans from the database. /// public void dropExpiredBans() { Database Database = new Database(true, true); if (Database.Ready) Database.runQuery("DELETE FROM moderation_bans WHERE expires > NOW()"); } #endregion #region Chatlog /// /// Adds a chat message to the chat log stack, so it will be written in the database on the next cycle. /// /// The ID of the session that chatted this message. /// The database ID of the user that chatted this message. /// The database ID of the room where this message was chatted in. /// If the type of this chat message was a whisper, the database ID of the receiving user should be supplied here. /// A value of the 'chatType' enum, representing the type of this chat message. /// The actual chat message. Pass this argument with the 'ref' keyword please. public void addChatMessageToLogStack(uint sessionID, int userID, int roomID, int receiverID, chatType Type, ref string Text) { if (mLogChat) { chatLog Log = new chatLog(sessionID, userID, roomID, receiverID, Type, Text); mChatLogStack.Add(Log); } } /// /// Processes the current chatlog stack and writes it into the 'moderation_chatlog' table of the database. /// private void processChatLogStack() { while (true) { Thread.Sleep(15000); // Every 15 seconds if (mChatLogStack.Count > 0) // Log chatlogs at min 10 logs @ stack { MySqlParameter dtParameter = new MySqlParameter("moment", MySqlDbType.DateTime); MySqlParameter txtParameter = new MySqlParameter("text", MySqlDbType.Text); Database dbClient = new Database(false, false); dbClient.addRawParameter(dtParameter); dbClient.addRawParameter(txtParameter); dbClient.Open(); if (dbClient.Ready) { lock (mChatLogStack) { try { foreach (chatLog Log in mChatLogStack) { dtParameter.Value = Log.Timestamp; txtParameter.Value = Log.Text; dbClient.runQuery(Log.ToString()); } } catch (Exception ex) { // Failed! Logging.Log("Error while inserting chatlogs: " + ex.ToString(), Logging.logType.commonError); } } dbClient.Close(); } mChatLogStack.Clear(); } } } #endregion #region Moderating methods public bool requestAlert(int userID, string targetUsername, string Message, string extraInfo) { int targetUserID = Engine.Game.Users.getUserID(targetUsername); if (targetUserID > 0) return requestAlert(userID, targetUserID, Message, extraInfo); else return false; } public bool requestAlert(int userID, int targetUserID, string Message, string extraInfo) { Session targetSession = Engine.Game.Users.getUserSession(targetUserID); if (targetSession != null) { targetSession.gameConnection.sendLocalizedError("mod_warn/" + Message); logModerationAction(userID, "alert", targetUserID, Message, extraInfo); return true; } return false; } public bool requestKickFromRoom(int userID, string targetUsername, string Message, string extraInfo) { int targetUserID = Engine.Game.Users.getUserID(targetUsername); if (targetUserID > 0) return requestKickFromRoom(userID, targetUserID, Message, extraInfo); else return false; } public bool requestKickFromRoom(int userID, int targetUserID, string Message, string extraInfo) { userInformation Caster = Engine.Game.Users.getUserInfo(userID, false); if (Caster != null) { Session targetSession = Engine.Game.Users.getUserSession(targetUserID); if (targetSession != null && targetSession.inRoom) { if (Caster.Role > targetSession.User.Role) { targetSession.kickFromRoom(Message); logModerationAction(Caster.ID, "kick", targetUserID, Message, extraInfo); return true; } } } return false; } public bool requestRoomAlert(int userID, int roomID, string Message, string extraInfo) { if (Engine.Game.Rooms.roomInstanceRunning(roomID)) { serverMessage Alert = new serverMessage(33); // "@a" Alert.Append("mod_warn/"); Alert.Append(Message); Engine.Game.Rooms.getRoomInstance(roomID).sendMessage(Alert); logModerationAction(userID, "roomalert", roomID, Message, extraInfo); return true; } return false; } public bool requestRoomKick(int userID, int roomID, string Message, string extraInfo) { userInformation Caster = Engine.Game.Users.getUserInfo(userID, false); if (Caster != null) { if (Engine.Game.Rooms.roomInstanceRunning(roomID)) { Engine.Game.Rooms.getRoomInstance(roomID).castRoomKick(Caster.Role, Message); logModerationAction(userID, "roomkick", roomID, Message, extraInfo); return true; } } return false; } public bool requestBan(int userID, string targetUsername, int banHours, bool banIP, bool banMachine, string Message, string extraInfo) { int targetUserID = Engine.Game.Users.getUserID(targetUsername); if (targetUserID > 0) return requestBan(userID, targetUserID, banHours, banIP, banMachine, Message, extraInfo); else return false; } public bool requestBan(int userID, int targetUserID, int banHours, bool banIP, bool banMachine, string Message, string extraInfo) { userInformation Caster = Engine.Game.Users.getUserInfo(userID, false); userInformation Target = Engine.Game.Users.getUserInfo(targetUserID, false); if (Caster != null && Target != null) { if (Caster.Role > Target.Role) // Able to ban this user { if (banHours >= 2 && banHours <= 100000 && !(banHours >= 168 && !Caster.hasFuseRight("fuse_superban"))) // Ban condition valid { userAccessInformation lastAccess = null; Session targetSession = Engine.Game.Users.getUserSession(targetUserID); #region Determine last access if (targetSession == null) lastAccess = Engine.Game.Users.getLastAccess(targetUserID); else lastAccess = targetSession.Access; if ((banIP || banMachine) && lastAccess == null) return false; // Can't ban IP and/or machine with no access log #endregion if (this.requestUnban(userID, targetUserID, lastAccess.IP, lastAccess.machineID)) // Removed old bans succeeded { #region Write ban entry Database dbClient = new Database(false, false); dbClient.addParameterWithValue("userid", targetUserID); dbClient.addParameterWithValue("appliedby", userID); dbClient.addParameterWithValue("expires", DateTime.Now.AddHours(banHours)); dbClient.addParameterWithValue("reason", Message); // Handle access parameters if (banIP) dbClient.addParameterWithValue("ip", lastAccess.IP); else dbClient.addParameterWithValue("ip", DBNull.Value); if (banMachine) dbClient.addParameterWithValue("machineid", lastAccess.machineID); else dbClient.addParameterWithValue("machineid", DBNull.Value); dbClient.Open(); if (!dbClient.Ready) return false; // Failed to write entry dbClient.runQuery("INSERT INTO moderation_bans VALUES (@userid,@ip,@machineid,NOW(),@appliedby,@expires,@reason)"); dbClient.Close(); dbClient = null; #endregion #region Notify & disconnect affected users List toNotify = new List(); if (targetSession != null) toNotify.Add(targetSession); if (banIP) Engine.Sessions.addSessionsByIpToList(ref toNotify, lastAccess.IP); serverMessage banCast = genericMessageFactory.createBanCast(Message); foreach (Session lSession in toNotify) { lSession.isValid = false; lSession.leaveRoom(true); lSession.gameConnection.sendMessage(banCast); } #endregion this.logModerationAction(userID, "ban", targetUserID, Message, extraInfo); return true; } } } } return false; } public bool requestUnban(int userID, string targetUsername, string IP, string machineID) { int targetUserID = Engine.Game.Users.getUserID(targetUsername); return requestUnban(userID, targetUserID, IP, machineID); } public bool requestUnban(int userID, int targetUserID, string IP, string machineID) { Database dbClient = new Database(false, false); dbClient.addParameterWithValue("userid", userID); dbClient.addParameterWithValue("targetid", targetUserID); dbClient.addParameterWithValue("ip", IP); dbClient.addParameterWithValue("machineid", machineID); dbClient.Open(); if (dbClient.Ready) { if (targetUserID > 0) dbClient.runQuery("DELETE FROM moderation_bans WHERE userid = @userid"); if (IP != null && machineID != null) dbClient.runQuery("DELETE FROM moderation_bans WHERE ip = @ip OR machineid = @machineid"); return true; } return false; } public void castHotelAlert(int userID, string Text) { Engine.Game.Users.broadcastHotelAlert(Text); this.logModerationAction(userID, "hotelalert", -1, Text, ""); } public void logModerationAction(int userID, string actionType, int targetID, string Message, string extraInfo) { Database Database = new Database(false, true); Database.addParameterWithValue("userid", userID); Database.addParameterWithValue("targetid", targetID); Database.addParameterWithValue("actiontype", actionType); Database.addParameterWithValue("message", Message); Database.addParameterWithValue("extra", extraInfo); Database.Open(); if (Database.Ready) Database.runQuery("INSERT INTO moderation_log VALUES (NOW(),@userid,@targetid,@actiontype,@message,@extra)"); Logging.Log("Staff member (user " + userID + ") performed action '" + actionType + "', target user/room was " + targetID + ".", Logging.logType.moderationEvent); } #endregion #endregion } }