From e385e661d47e7e160a1391c164f7fa98a09109e2 Mon Sep 17 00:00:00 2001 From: zaCade Date: Fri, 27 Jan 2023 16:21:44 +0100 Subject: [PATCH] [SB++] Initialize with version 1.6.4 --- .../configs/sourcebans/sb_admin_groups.cfg | 0 Sourcebans++/configs/sourcebans/sb_admins.cfg | 0 .../configs/sourcebans/sourcebans.cfg | 105 + .../configs/sourcebans/sourcecomms.cfg | 66 + .../sourcebans/sourcesleuth_whitelist.cfg | 1 + .../scripting/include/sourcebanspp.inc | 106 + .../scripting/include/sourcecomms.inc | 128 + Sourcebans++/scripting/sbpp_admcfg.sp | 107 + .../sbpp_admcfg/sbpp_admin_groups.sp | 454 +++ .../scripting/sbpp_admcfg/sbpp_admin_users.sp | 464 +++ Sourcebans++/scripting/sbpp_checker.sp | 564 +++ Sourcebans++/scripting/sbpp_comms.sp | 3279 +++++++++++++++++ Sourcebans++/scripting/sbpp_main.sp | 2750 ++++++++++++++ Sourcebans++/scripting/sbpp_report.sp | 199 + Sourcebans++/scripting/sbpp_sleuth.sp | 281 ++ .../translations/sourcebans.phrases.txt | 149 + .../translations/sourcecomms.phrases.txt | 1241 +++++++ .../translations/sourcereport.phrases.txt | 44 + .../translations/sourcesleuth.phrases.txt | 25 + 19 files changed, 9963 insertions(+) create mode 100644 Sourcebans++/configs/sourcebans/sb_admin_groups.cfg create mode 100644 Sourcebans++/configs/sourcebans/sb_admins.cfg create mode 100644 Sourcebans++/configs/sourcebans/sourcebans.cfg create mode 100644 Sourcebans++/configs/sourcebans/sourcecomms.cfg create mode 100644 Sourcebans++/configs/sourcebans/sourcesleuth_whitelist.cfg create mode 100644 Sourcebans++/scripting/include/sourcebanspp.inc create mode 100644 Sourcebans++/scripting/include/sourcecomms.inc create mode 100644 Sourcebans++/scripting/sbpp_admcfg.sp create mode 100644 Sourcebans++/scripting/sbpp_admcfg/sbpp_admin_groups.sp create mode 100644 Sourcebans++/scripting/sbpp_admcfg/sbpp_admin_users.sp create mode 100644 Sourcebans++/scripting/sbpp_checker.sp create mode 100644 Sourcebans++/scripting/sbpp_comms.sp create mode 100644 Sourcebans++/scripting/sbpp_main.sp create mode 100644 Sourcebans++/scripting/sbpp_report.sp create mode 100644 Sourcebans++/scripting/sbpp_sleuth.sp create mode 100644 Sourcebans++/translations/sourcebans.phrases.txt create mode 100644 Sourcebans++/translations/sourcecomms.phrases.txt create mode 100644 Sourcebans++/translations/sourcereport.phrases.txt create mode 100644 Sourcebans++/translations/sourcesleuth.phrases.txt diff --git a/Sourcebans++/configs/sourcebans/sb_admin_groups.cfg b/Sourcebans++/configs/sourcebans/sb_admin_groups.cfg new file mode 100644 index 0000000..e69de29 diff --git a/Sourcebans++/configs/sourcebans/sb_admins.cfg b/Sourcebans++/configs/sourcebans/sb_admins.cfg new file mode 100644 index 0000000..e69de29 diff --git a/Sourcebans++/configs/sourcebans/sourcebans.cfg b/Sourcebans++/configs/sourcebans/sourcebans.cfg new file mode 100644 index 0000000..99be670 --- /dev/null +++ b/Sourcebans++/configs/sourcebans/sourcebans.cfg @@ -0,0 +1,105 @@ +/** +* sourcebans.cfg +* +* This file contains settings for the SourceBans Source Server Plugin +* @author SteamFriends Development Team +* @version 0.0.0.$Rev: 74 $ +* @copyright SteamFriends (www.steamfriends.com) +* @package SourceBans +*/ + +"SourceBans" +{ + "Config" + { + // Website address to tell where the player to go for unban, etc + "Website" "http://www.yourwebsite.net/" + + // Allow or disallow admins access to addban command + "Addban" "1" + + // Allow or disallow admins access to unban command + "Unban" "1" + + // The Tableprefix you set while installing the webpanel. (default: "sb") + "DatabasePrefix" "sb" + + // How many seconds to wait before retrying when a players ban fails to be checked. Min = 15.0 Max = 60.0 + "RetryTime" "45.0" + + // How often should we process the failed ban queue in minutes + "ProcessQueueTime" "5" + + // Should the plugin automaticaly add the server to sourcebans + // (servers without -ip being set on startup need this set to 0) + "AutoAddServer" "0" + + // Enable backing up config files after getting admins from database (1 = enabled, 0 = disabled) + "BackupConfigs" "1" + + // Enable admin part of the plugin (1 = enabled, 0 = disabled) + "EnableAdmins" "1" + + // Require the admin to login once into website + "RequireSiteLogin" "0" + + // This is the ID of this server (Check in the admin panel -> servers to find the ID of this server) + "ServerID" "-1" + } + + /* + * Generic menu options for if a reason isn't supplied in a ban + * Without a supplied reason the ban will never be written to the database + */ + "BanReasons" + { + "Hacking" "Hacking" + "Exploit" "General Exploit of Game/Map/Server" + "TK" "Team Killing" + "TF" "Team Flashing" + "CommSpam" "Spamming Mic/Chat" + "BadSpray" "Inappropriate Spray" + "BadLang" "Inappropriate Language" + "BadName" "Inappropriate Name" + "IgnoreAdmin" "Ignoring Admins" + "Stacking" "Team Stacking" + "Own Reason" "Own Reason" + } + + /* + * Submenu options for when "Hacking" is selected + * If "Hacking" is removed from the menu above this will not be accessable + */ + "HackingReasons" + { + "Aimbot" "Aimbot" + "Antirecoil" "Anti Recoil" + "Wallhack" "Wallhack" + "Spinhack" "Spinhack" + "Speedhack" "Speedhack" + "Multi-Hack" "Multi-Hack" + "No Smoke" "No Smoke" + "No Flash" "No Flash" + } + + /* + * Available time for bans. + * Permanent (0) available only for admins with access for command "sm_unban" + */ + "BanTime" + { + // "time in minutes" "display text" + + "0" "Permanent" + "10" "10 Minutes" + "30" "30 Minutes" + "60" "1 Hour" + "240" "4 Hours" + "1440" "1 Day" + "10080" "1 Week" + + // Examples: + // "43200" "1 Month" + // "525600" "1 Year" + } +} \ No newline at end of file diff --git a/Sourcebans++/configs/sourcebans/sourcecomms.cfg b/Sourcebans++/configs/sourcebans/sourcecomms.cfg new file mode 100644 index 0000000..54b6889 --- /dev/null +++ b/Sourcebans++/configs/sourcebans/sourcecomms.cfg @@ -0,0 +1,66 @@ +/** +* sourcecomms.cfg +* +* This file contains settings for the SourceComms Plugin +*/ + +"SourceComms" +{ + "Config" + { + "DefaultTime" "30" // default time in minutes. if < 0 -> blocking for session. Permanent (0) - is not allowed! + "DisableUnblockImmunityCheck" "0" // 0, 1. If 1, player can be ungagged only by issuer admin, console or admin with special flag + // Also, If 0 player maybe unblocked by Admin with higher immunity level then issuer admin. + // Default value is 0 + "ConsoleImmunity" "20" // Immunity Level of server console. If not specified - 0. + "MaxLength" "0" // Max allowed punishment length (in minutes) for admins without ADMFLAG_CUSTOM2 (p). + // 0 disables restriction. Any value > 0 restricts permanent punishment. + "OnlyWhiteListServers" "0" // Set this option to 1 to applying on players punishments only from servers listed in WhiteList and this server. + // 0 applies on players punishments from any server. + } + + "CommsReasons" + { + //Generic menu options for if a reason isn't supplied in a block + //-------------------------------------------------------------// + // "Reason to store in DB" "Reason to display in menu" // + //-------------------------------------------------------------// + "Obscene language" "Obscene language" + "Insult players" "Insult players" + "Admin disrespect" "Admin disrespect" + "Inappropriate Language" "Inappropriate Language" + "Spam in chat/voice" "Spam" + "Trading" "Trading" + "Other" "Other" + "Advertisement" "Advertisement" + "Played music in voice" "Music in voice" + } + + "CommsTimes" + { + // Times to show in duration menu // + //-----------------------------------------------// + // "Time in minutes" "Time to display in menu" // + //-----------------------------------------------// + "-1" "Session" // If time < 0 -> blocking comms for player session + "30" "30 minutes" + "60" "60 minutes" + "120" "2 hours" + "360" "6 hours" + "720" "12 hours" + "1440" "24 hours" + "2880" "2 days" + "10080" "7 days" + "20160" "2 weeks" + "0" "Permanent" + } + + "ServersWhiteList" + { + //-----------------------------------------// + // "id" "ServerID from sourcebans.cfg" // + //-----------------------------------------// + "id" "0" // Web Punishments (from sourcebans web pages) +// "id" "3" // for example: uncommenting this line will add server with ServerID 3 to white list. + } +} \ No newline at end of file diff --git a/Sourcebans++/configs/sourcebans/sourcesleuth_whitelist.cfg b/Sourcebans++/configs/sourcebans/sourcesleuth_whitelist.cfg new file mode 100644 index 0000000..4f13dcc --- /dev/null +++ b/Sourcebans++/configs/sourcebans/sourcesleuth_whitelist.cfg @@ -0,0 +1 @@ +//STEAM_1:0:8055392 \ No newline at end of file diff --git a/Sourcebans++/scripting/include/sourcebanspp.inc b/Sourcebans++/scripting/include/sourcebanspp.inc new file mode 100644 index 0000000..88a1461 --- /dev/null +++ b/Sourcebans++/scripting/include/sourcebanspp.inc @@ -0,0 +1,106 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 SourceBans++ Dev Team +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file based off work(s) covered by the following copyright(s): +// +// SourceBans 1.4.11 +// Copyright (C) 2007-2015 SourceBans Team - Part of GameConnect +// Licensed under GNU GPL version 3, or later. +// Page: - +// +// ************************************************************************* + +#if defined _sourcebanspp_included +#endinput +#endif +#define _sourcebanspp_included + +public SharedPlugin __pl_sourcebanspp = +{ + name = "sourcebans++", + file = "sbpp_main.smx", + #if defined REQUIRE_PLUGIN + required = 1 + #else + required = 0 + #endif +}; + +#if !defined REQUIRE_PLUGIN +public __pl_sourcebanspp_SetNTVOptional() +{ + MarkNativeAsOptional("SBBanPlayer"); + MarkNativeAsOptional("SBPP_BanPlayer"); + MarkNativeAsOptional("SBPP_ReportPlayer"); +} +#endif + + +/********************************************************* + * Ban Player from server + * + * @param iAdmin The client index of the admin who is banning the client + * @param iTarget The client index of the player to ban + * @param iTime The time to ban the player for (in minutes, 0 = permanent) + * @param sReason The reason to ban the player from the server + * @noreturn + *********************************************************/ +#pragma deprecated Use SBPP_BanPlayer() instead. +native void SBBanPlayer(int iAdmin, int iTarget, int iTime, const char[] sReason); + +/********************************************************* + * Ban Player from server + * + * @param iAdmin The client index of the admin who is banning the client + * @param iTarget The client index of the player to ban + * @param iTime The time to ban the player for (in minutes, 0 = permanent) + * @param sReason The reason to ban the player from the server + * @noreturn + *********************************************************/ +native void SBPP_BanPlayer(int iAdmin, int iTarget, int iTime, const char[] sReason); + +/********************************************************* + * Reports a player + * + * @param iReporter The client index of the reporter + * @param iTarget The client index of the player to report + * @param sReason The reason to report the player + * @noreturn + *********************************************************/ +native void SBPP_ReportPlayer(int iReporter, int iTarget, const char[] sReason); + +/********************************************************* + * Called when the admin banning the player. + * + * @param iAdmin The client index of the admin who is banning the client + * @param iTarget The client index of the player to ban + * @param iTime The time to ban the player for (in minutes, 0 = permanent) + * @param sReason The reason to ban the player from the server + *********************************************************/ +forward void SBPP_OnBanPlayer(int iAdmin, int iTarget, int iTime, const char[] sReason); + +/********************************************************* + * Called when a new report is inserted + * + * @param iReporter The client index of the reporter + * @param iTarget The client index of the player to report + * @param sReason The reason to report the player + * @noreturn + *********************************************************/ +forward void SBPP_OnReportPlayer(int iReporter, int iTarget, const char[] sReason); + +//Yarr! diff --git a/Sourcebans++/scripting/include/sourcecomms.inc b/Sourcebans++/scripting/include/sourcecomms.inc new file mode 100644 index 0000000..c92f3a6 --- /dev/null +++ b/Sourcebans++/scripting/include/sourcecomms.inc @@ -0,0 +1,128 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 SourceBans++ Dev Team +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file based off work(s) covered by the following copyright(s): +// +// SourceComms 0.9.266 +// Copyright (C) 2013-2014 Alexandr Duplishchev +// Licensed under GNU GPL version 3, or later. +// Page: - +// +// ************************************************************************* + +#if defined _sourcecomms_included +#endinput +#endif +#define _sourcecomms_included + +/** + * @section Int definitions for punishments types. + */ + +#define TYPE_MUTE 1 /**< Voice Mute */ +#define TYPE_GAG 2 /**< Gag (text chat) */ +#define TYPE_SILENCE 3 /**< Silence (mute + gag) */ +#define TYPE_UNMUTE 4 /**< Voice Unmute*/ +#define TYPE_UNGAG 5 /**< Ungag*/ +#define TYPE_UNSILENCE 6 /**< Unsilence */ +#define TYPE_TEMP_UNMUTE 14 /**< Temp mute removed */ +#define TYPE_TEMP_UNGAG 15 /**< Temp gag removed */ +#define TYPE_TEMP_UNSILENCE 16 /**< Temp silence removed */ + +/* Punishments types */ +enum bType { + bNot = 0, // Player chat or voice is not blocked + bSess, // ... blocked for player session (until reconnect) + bTime, // ... blocked for some time + bPerm // ... permanently blocked +} + +/** + * Sets a client's mute state. + * + * @param client Client index. + * @param muteState True to mute client, false to unmute. + * -------------------------------------Parameters below this line are used only for muteState=true------------------------------------- + * ----------------------------------for muteState=false these parameters are ignored (saveToDB=false)---------------------------------- + * @param muteLength Length of punishment in minutes. Value < 0 muting client for session. Permanent (0) is not allowed at this time. + * @param saveToDB If true, punishment will be saved in database. + * @param reason Reason for punishment. + * @return True if this caused a change in mute state, false otherwise. + */ +native bool:SourceComms_SetClientMute(client, bool:muteState, muteLength = -1, bool:saveToDB = false, const String:reason[] = "Muted through natives"); + +/** + * Sets a client's gag state. + * + * @param client Client index. + * @param gagState True to gag client, false to ungag. + * --------------------------------------Parameters below this line are used only for gagState=true-------------------------------------- + * -----------------------------------for gagState=false these parameters are ignored (saveToDB=false)----------------------------------- + * @param gagLength Length of punishment in minutes. Value < 0 gagging client for session. Permanent (0) is not allowed at this time. + * @param saveToDB If true, punishment will be saved in database. + * @param reason Reason for punishment. + * @return True if this caused a change in gag state, false otherwise. + */ +native bool:SourceComms_SetClientGag(client, bool:gagState, gagLength = -1, bool:saveToDB = false, const String:reason[] = "Gagged through natives"); + +/** + * Returns the client's mute type + * + * @param client The client index of the player to check mute status + * @return The client's current mute type index (see enum bType in the begin). + */ +native bType:SourceComms_GetClientMuteType(client); + + +/** + * Returns the client's gag type + * + * @param client The client index of the player to check gag status + * @return The client's current gag type index (see enum bType in the begin). + */ +native bType:SourceComms_GetClientGagType(client); + +/** + * Called when added communication block for player. + * + * @param client The client index of the admin who is blocking the client. + * @param target The client index of the player to blocked. + * @param time The time to blocked the player for (in minutes, 0 = permanent). + * @param type The type of block. See section "Int definitions for punishments types". + * @param reason The reason to block the player. + */ +forward SourceComms_OnBlockAdded(client, target, time, type, String:reason[]); + +public SharedPlugin:__pl_sourcecomms = +{ + name = "sourcecomms++", + file = "sbpp_comms.smx", + #if defined REQUIRE_PLUGIN + required = 1 + #else + required = 0 + #endif +}; + +public __pl_sourcecomms_SetNTVOptional() +{ + MarkNativeAsOptional("SourceComms_SetClientMute"); + MarkNativeAsOptional("SourceComms_SetClientGag"); + MarkNativeAsOptional("SourceComms_GetClientMuteType"); + MarkNativeAsOptional("SourceComms_GetClientGagType"); + +} diff --git a/Sourcebans++/scripting/sbpp_admcfg.sp b/Sourcebans++/scripting/sbpp_admcfg.sp new file mode 100644 index 0000000..f47dd13 --- /dev/null +++ b/Sourcebans++/scripting/sbpp_admcfg.sp @@ -0,0 +1,107 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 SourceBans++ Dev Team +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file is based off work(s) covered by the following copyright(s): +// +// SourceMod Admin File Reader Plugin +// Copyright (C) 2004-2008 AlliedModders LLC +// Licensed under GNU GPL version 3 +// Page: +// +// ************************************************************************* + +#pragma semicolon 1 + +#include + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 7 +public Plugin:myinfo = +#else +public Plugin myinfo = +#endif +{ + name = "SourceBans++: Admin Config Loader", + author = "AlliedModders LLC, SourceBans++ Dev Team", + description = "Reads Admin Files", + version = "1.6.4", + url = "https://sbpp.github.io" +}; + + +/** Various parsing globals */ +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 7 +new bool:g_LoggedFileName = false; /* Whether or not the file name has been logged */ +new g_ErrorCount = 0; /* Current error count */ +new g_IgnoreLevel = 0; /* Nested ignored section count, so users can screw up files safely */ +new g_CurrentLine = 0; /* Current line we're on */ +new String:g_Filename[PLATFORM_MAX_PATH]; /* Used for error messages */ +#else +bool g_LoggedFileName = false; /* Whether or not the file name has been logged */ +int g_ErrorCount = 0; /* Current error count */ +int g_IgnoreLevel = 0; /* Nested ignored section count, so users can screw up files safely */ +int g_CurrentLine = 0; /* Current line we're on */ +char g_Filename[PLATFORM_MAX_PATH]; /* Used for error messages */ +#endif + +#include "sbpp_admcfg/sbpp_admin_groups.sp" +#include "sbpp_admcfg/sbpp_admin_users.sp" + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 7 +public OnRebuildAdminCache(AdminCachePart:part) +#else +public void OnRebuildAdminCache(AdminCachePart part) +#endif +{ + if (part == AdminCache_Groups) { + ReadGroups(); + } else if (part == AdminCache_Admins) { + ReadUsers(); + } +} + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 7 +ParseError(const String:format[], any:...) +#else +void ParseError(const char[] format, any...) +#endif +{ + #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 7 + decl String:buffer[512]; + #else + char buffer[512]; + #endif + + if (!g_LoggedFileName) + { + LogError("Error(s) Detected Parsing %s", g_Filename); + g_LoggedFileName = true; + } + + VFormat(buffer, sizeof(buffer), format, 2); + + LogError(" (line %d) %s", g_CurrentLine, buffer); + + g_ErrorCount++; +} + +void InitGlobalStates() +{ + g_ErrorCount = 0; + g_IgnoreLevel = 0; + g_CurrentLine = 0; + g_LoggedFileName = false; +} diff --git a/Sourcebans++/scripting/sbpp_admcfg/sbpp_admin_groups.sp b/Sourcebans++/scripting/sbpp_admcfg/sbpp_admin_groups.sp new file mode 100644 index 0000000..33be215 --- /dev/null +++ b/Sourcebans++/scripting/sbpp_admcfg/sbpp_admin_groups.sp @@ -0,0 +1,454 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 SourceBans++ Dev Team +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file based off work(s) covered by the following copyright(s): +// +// SourceMod Admin File Reader Plugin +// Copyright (C) 2004-2008 AlliedModders LLC +// Licensed under GNU GPL version 3 +// Page: +// +// ************************************************************************* + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 7 +#define GROUP_STATE_NONE 0 +#define GROUP_STATE_GROUPS 1 +#define GROUP_STATE_INGROUP 2 +#define GROUP_STATE_OVERRIDES 3 +#define GROUP_PASS_FIRST 1 +#define GROUP_PASS_SECOND 2 + +static SMCParser g_hGroupParser; +static GroupId:g_CurGrp = INVALID_GROUP_ID; +static g_GroupState = GROUP_STATE_NONE; +static g_GroupPass = 0; +static bool:g_NeedReparse = false; + +public SMCResult ReadGroups_NewSection(SMCParser smc, const char[] name, bool opt_quotes) +{ + if (g_IgnoreLevel) + { + g_IgnoreLevel++; + return SMCParse_Continue; + } + + if (g_GroupState == GROUP_STATE_NONE) + { + if (StrEqual(name, "Groups", false)) + { + g_GroupState = GROUP_STATE_GROUPS; + } else { + g_IgnoreLevel++; + } + } else if (g_GroupState == GROUP_STATE_GROUPS) { + if ((g_CurGrp = CreateAdmGroup(name)) == INVALID_GROUP_ID) + { + g_CurGrp = FindAdmGroup(name); + } + g_GroupState = GROUP_STATE_INGROUP; + } else if (g_GroupState == GROUP_STATE_INGROUP) { + if (StrEqual(name, "Overrides", false)) + { + g_GroupState = GROUP_STATE_OVERRIDES; + } else { + g_IgnoreLevel++; + } + } else { + g_IgnoreLevel++; + } + + return SMCParse_Continue; +} + +public SMCResult ReadGroups_KeyValue(SMCParser smc, + const char[] key, + const char[] value, + bool key_quotes, + bool value_quotes) +{ + if (g_CurGrp == INVALID_GROUP_ID || g_IgnoreLevel) + { + return SMCParse_Continue; + } + + new AdminFlag:flag; + + if (g_GroupPass == GROUP_PASS_FIRST) + { + if (g_GroupState == GROUP_STATE_INGROUP) + { + if (StrEqual(key, "flags", false)) + { + new len = strlen(value); + for (new i = 0; i < len; i++) + { + if (!FindFlagByChar(value[i], flag)) + { + continue; + } + SetAdmGroupAddFlag(g_CurGrp, flag, true); + } + } else if (StrEqual(key, "immunity", false)) { + g_NeedReparse = true; + } + } else if (g_GroupState == GROUP_STATE_OVERRIDES) { + new OverrideRule:rule = Command_Deny; + + if (StrEqual(value, "allow", false)) + { + rule = Command_Allow; + } + + if (key[0] == '@') + { + AddAdmGroupCmdOverride(g_CurGrp, key[1], Override_CommandGroup, rule); + } else { + AddAdmGroupCmdOverride(g_CurGrp, key, Override_Command, rule); + } + } + } else if (g_GroupPass == GROUP_PASS_SECOND + && g_GroupState == GROUP_STATE_INGROUP) { + /* Check for immunity again, core should handle double inserts */ + if (StrEqual(key, "immunity", false)) + { + /* If it's a value we know about, use it */ + if (StrEqual(value, "*")) + { + SetAdmGroupImmunityLevel(g_CurGrp, 2); + } else if (StrEqual(value, "$")) { + SetAdmGroupImmunityLevel(g_CurGrp, 1); + } else { + new level; + if (StringToIntEx(value, level)) + { + SetAdmGroupImmunityLevel(g_CurGrp, level); + } else { + new GroupId:id; + if (value[0] == '@') + { + id = FindAdmGroup(value[1]); + } else { + id = FindAdmGroup(value); + } + if (id != INVALID_GROUP_ID) + { + SetAdmGroupImmuneFrom(g_CurGrp, id); + } else { + ParseError("Unable to find group: \"%s\"", value); + } + } + } + } + } + + return SMCParse_Continue; +} + +public SMCResult ReadGroups_EndSection(SMCParser smc) +{ + /* If we're ignoring, skip out */ + if (g_IgnoreLevel) + { + g_IgnoreLevel--; + return SMCParse_Continue; + } + + if (g_GroupState == GROUP_STATE_OVERRIDES) + { + g_GroupState = GROUP_STATE_INGROUP; + } else if (g_GroupState == GROUP_STATE_INGROUP) { + g_GroupState = GROUP_STATE_GROUPS; + g_CurGrp = INVALID_GROUP_ID; + } else if (g_GroupState == GROUP_STATE_GROUPS) { + g_GroupState = GROUP_STATE_NONE; + } + + return SMCParse_Continue; +} + +public SMCResult ReadGroups_CurrentLine(SMCParser smc, const char[] line, int lineno) +{ + g_CurrentLine = lineno; + + return SMCParse_Continue; +} + +static InitializeGroupParser() +{ + if (!g_hGroupParser) + { + g_hGroupParser = new SMCParser(); + g_hGroupParser.OnEnterSection = ReadGroups_NewSection; + g_hGroupParser.OnKeyValue = ReadGroups_KeyValue; + g_hGroupParser.OnLeaveSection = ReadGroups_EndSection; + g_hGroupParser.OnRawLine = ReadGroups_CurrentLine; + } +} + +static InternalReadGroups(const String:path[], pass) +{ + /* Set states */ + InitGlobalStates(); + g_GroupState = GROUP_STATE_NONE; + g_CurGrp = INVALID_GROUP_ID; + g_GroupPass = pass; + g_NeedReparse = false; + + SMCError err = g_hGroupParser.ParseFile(path); + if (err != SMCError_Okay) + { + char buffer[64]; + if (g_hGroupParser.GetErrorString(err, buffer, sizeof(buffer))) + { + ParseError("%s", buffer); + } else { + ParseError("Fatal parse error"); + } + } +} + +ReadGroups() +{ + InitializeGroupParser(); + + BuildPath(Path_SM, g_Filename, sizeof(g_Filename), "configs/sourcebans/sb_admin_groups.cfg"); + + InternalReadGroups(g_Filename, GROUP_PASS_FIRST); + if (g_NeedReparse) + { + InternalReadGroups(g_Filename, GROUP_PASS_SECOND); + } +} +/* SOURCEMOD 1.7 PLUGIN STOPS HERE */ +#else +enum GroupState +{ + GroupState_None, + GroupState_Groups, + GroupState_InGroup, + GroupState_Overrides, +} + +enum GroupPass +{ + GroupPass_Invalid, + GroupPass_First, + GroupPass_Second, +} + +static SMCParser g_hGroupParser; +static GroupId g_CurGrp = INVALID_GROUP_ID; +static GroupState g_GroupState = GroupState_None; +static GroupPass g_GroupPass = GroupPass_Invalid; +static bool g_NeedReparse = false; + +public SMCResult ReadGroups_NewSection(SMCParser smc, const char[] name, bool opt_quotes) +{ + if (g_IgnoreLevel) + { + g_IgnoreLevel++; + return SMCParse_Continue; + } + + if (g_GroupState == GroupState_None) + { + if (StrEqual(name, "Groups", false)) + { + g_GroupState = GroupState_Groups; + } else { + g_IgnoreLevel++; + } + } else if (g_GroupState == GroupState_Groups) { + if ((g_CurGrp = CreateAdmGroup(name)) == INVALID_GROUP_ID) + { + g_CurGrp = FindAdmGroup(name); + } + g_GroupState = GroupState_InGroup; + } else if (g_GroupState == GroupState_InGroup) { + if (StrEqual(name, "Overrides", false)) + { + g_GroupState = GroupState_Overrides; + } else { + g_IgnoreLevel++; + } + } else { + g_IgnoreLevel++; + } + + return SMCParse_Continue; +} + +public SMCResult ReadGroups_KeyValue(SMCParser smc, + const char[] key, + const char[] value, + bool key_quotes, + bool value_quotes) +{ + if (g_CurGrp == INVALID_GROUP_ID || g_IgnoreLevel) + { + return SMCParse_Continue; + } + + AdminFlag flag; + + if (g_GroupPass == GroupPass_First) + { + if (g_GroupState == GroupState_InGroup) + { + if (StrEqual(key, "flags", false)) + { + int len = strlen(value); + for (int i = 0; i < len; i++) + { + if (!FindFlagByChar(value[i], flag)) + { + continue; + } + g_CurGrp.SetFlag(flag, true); + } + } else if (StrEqual(key, "immunity", false)) { + g_NeedReparse = true; + } + } else if (g_GroupState == GroupState_Overrides) { + OverrideRule rule = Command_Deny; + + if (StrEqual(value, "allow", false)) + { + rule = Command_Allow; + } + + if (key[0] == '@') + { + g_CurGrp.AddCommandOverride(key[1], Override_CommandGroup, rule); + } else { + g_CurGrp.AddCommandOverride(key, Override_Command, rule); + } + } + } else if (g_GroupPass == GroupPass_Second + && g_GroupState == GroupState_InGroup) { + /* Check for immunity again, core should handle double inserts */ + if (StrEqual(key, "immunity", false)) + { + /* If it's a value we know about, use it */ + if (StrEqual(value, "*")) + { + g_CurGrp.ImmunityLevel = 2; + } else if (StrEqual(value, "$")) { + g_CurGrp.ImmunityLevel = 1; + } else { + int level; + if (StringToIntEx(value, level)) + { + g_CurGrp.ImmunityLevel = level; + } else { + GroupId id; + if (value[0] == '@') + { + id = FindAdmGroup(value[1]); + } else { + id = FindAdmGroup(value); + } + if (id != INVALID_GROUP_ID) + { + g_CurGrp.AddGroupImmunity(id); + } else { + ParseError("Unable to find group: \"%s\"", value); + } + } + } + } + } + + return SMCParse_Continue; +} + +public SMCResult ReadGroups_EndSection(SMCParser smc) +{ + /* If we're ignoring, skip out */ + if (g_IgnoreLevel) + { + g_IgnoreLevel--; + return SMCParse_Continue; + } + + if (g_GroupState == GroupState_Overrides) + { + g_GroupState = GroupState_InGroup; + } else if (g_GroupState == GroupState_InGroup) { + g_GroupState = GroupState_Groups; + g_CurGrp = INVALID_GROUP_ID; + } else if (g_GroupState == GroupState_Groups) { + g_GroupState = GroupState_None; + } + + return SMCParse_Continue; +} + +public SMCResult ReadGroups_CurrentLine(SMCParser smc, const char[] line, int lineno) +{ + g_CurrentLine = lineno; + + return SMCParse_Continue; +} + +static void InitializeGroupParser() +{ + if (!g_hGroupParser) + { + g_hGroupParser = new SMCParser(); + g_hGroupParser.OnEnterSection = ReadGroups_NewSection; + g_hGroupParser.OnKeyValue = ReadGroups_KeyValue; + g_hGroupParser.OnLeaveSection = ReadGroups_EndSection; + g_hGroupParser.OnRawLine = ReadGroups_CurrentLine; + } +} + +static void InternalReadGroups(const char[] path, GroupPass pass) +{ + /* Set states */ + InitGlobalStates(); + g_GroupState = GroupState_None; + g_CurGrp = INVALID_GROUP_ID; + g_GroupPass = pass; + g_NeedReparse = false; + + SMCError err = g_hGroupParser.ParseFile(path); + if (err != SMCError_Okay) + { + char buffer[64]; + if (g_hGroupParser.GetErrorString(err, buffer, sizeof(buffer))) + { + ParseError("%s", buffer); + } else { + ParseError("Fatal parse error"); + } + } +} + +void ReadGroups() +{ + InitializeGroupParser(); + + BuildPath(Path_SM, g_Filename, sizeof(g_Filename), "configs/sourcebans/sb_admin_groups.cfg"); + + InternalReadGroups(g_Filename, GroupPass_First); + if (g_NeedReparse) + { + InternalReadGroups(g_Filename, GroupPass_Second); + } +} +#endif \ No newline at end of file diff --git a/Sourcebans++/scripting/sbpp_admcfg/sbpp_admin_users.sp b/Sourcebans++/scripting/sbpp_admcfg/sbpp_admin_users.sp new file mode 100644 index 0000000..80dd1b4 --- /dev/null +++ b/Sourcebans++/scripting/sbpp_admcfg/sbpp_admin_users.sp @@ -0,0 +1,464 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 SourceBans++ Dev Team +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file is based off work(s) covered by the following copyright(s): +// +// SourceMod Admin File Reader Plugin +// Copyright (C) 2004-2008 AlliedModders LLC +// Licensed under GNU GPL version 3 +// Page: +// +// ************************************************************************* + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 7 +#define USER_STATE_NONE 0 +#define USER_STATE_ADMINS 1 +#define USER_STATE_INADMIN 2 + +static SMCParser g_hUserParser; +static g_UserState = USER_STATE_NONE; +static String:g_CurAuth[64]; +static String:g_CurIdent[64]; +static String:g_CurName[64]; +static String:g_CurPass[64]; +static Handle:g_GroupArray; +static g_CurFlags; +static g_CurImmunity; + +public SMCResult:ReadUsers_NewSection(Handle:smc, const String:name[], bool:opt_quotes) +{ + if (g_IgnoreLevel) + { + g_IgnoreLevel++; + return SMCParse_Continue; + } + + if (g_UserState == USER_STATE_NONE) + { + if (StrEqual(name, "Admins", false)) + { + g_UserState = USER_STATE_ADMINS; + } + else + { + g_IgnoreLevel++; + } + } + else if (g_UserState == USER_STATE_ADMINS) + { + g_UserState = USER_STATE_INADMIN; + strcopy(g_CurName, sizeof(g_CurName), name); + g_CurAuth[0] = '\0'; + g_CurIdent[0] = '\0'; + g_CurPass[0] = '\0'; + ClearArray(g_GroupArray); + g_CurFlags = 0; + g_CurImmunity = 0; + } + else + { + g_IgnoreLevel++; + } + + return SMCParse_Continue; +} + +public SMCResult:ReadUsers_KeyValue(Handle:smc, + const String:key[], + const String:value[], + bool:key_quotes, + bool:value_quotes) +{ + if (g_UserState != USER_STATE_INADMIN || g_IgnoreLevel) + { + return SMCParse_Continue; + } + + if (StrEqual(key, "auth", false)) + { + strcopy(g_CurAuth, sizeof(g_CurAuth), value); + } + else if (StrEqual(key, "identity", false)) + { + strcopy(g_CurIdent, sizeof(g_CurIdent), value); + } + else if (StrEqual(key, "password", false)) + { + strcopy(g_CurPass, sizeof(g_CurPass), value); + } + else if (StrEqual(key, "group", false)) + { + new GroupId:id = FindAdmGroup(value); + if (id == INVALID_GROUP_ID) + { + ParseError("Unknown group \"%s\"", value); + } + + PushArrayCell(g_GroupArray, id); + } + else if (StrEqual(key, "flags", false)) + { + new len = strlen(value); + new AdminFlag:flag; + + for (new i = 0; i < len; i++) + { + if (!FindFlagByChar(value[i], flag)) + { + ParseError("Invalid flag detected: %c", value[i]); + } + else + { + g_CurFlags |= FlagToBit(flag); + } + } + } + else if (StrEqual(key, "immunity", false)) + { + g_CurImmunity = StringToInt(value); + } + + return SMCParse_Continue; +} + +public SMCResult:ReadUsers_EndSection(Handle:smc) +{ + if (g_IgnoreLevel) + { + g_IgnoreLevel--; + return SMCParse_Continue; + } + + if (g_UserState == USER_STATE_INADMIN) + { + /* Dump this user to memory */ + if (g_CurIdent[0] != '\0' && g_CurAuth[0] != '\0') + { + decl AdminFlag:flags[26]; + new AdminId:id, i, num_groups, num_flags; + + if ((id = FindAdminByIdentity(g_CurAuth, g_CurIdent)) == INVALID_ADMIN_ID) + { + id = CreateAdmin(g_CurName); + if (!BindAdminIdentity(id, g_CurAuth, g_CurIdent)) + { + RemoveAdmin(id); + ParseError("Failed to bind auth \"%s\" to identity \"%s\"", g_CurAuth, g_CurIdent); + return SMCParse_Continue; + } + } + + num_groups = GetArraySize(g_GroupArray); + for (i = 0; i < num_groups; i++) + { + AdminInheritGroup(id, GetArrayCell(g_GroupArray, i)); + } + + SetAdminPassword(id, g_CurPass); + if (GetAdminImmunityLevel(id) < g_CurImmunity) + { + SetAdminImmunityLevel(id, g_CurImmunity); + } + + num_flags = FlagBitsToArray(g_CurFlags, flags, sizeof(flags)); + for (i = 0; i < num_flags; i++) + { + SetAdminFlag(id, flags[i], true); + } + } + else + { + ParseError("Failed to create admin: did you forget either the auth or identity properties?"); + } + + g_UserState = USER_STATE_ADMINS; + } + else if (g_UserState == USER_STATE_ADMINS) + { + g_UserState = USER_STATE_NONE; + } + + return SMCParse_Continue; +} + +public SMCResult:ReadUsers_CurrentLine(Handle:smc, const String:line[], lineno) +{ + g_CurrentLine = lineno; + + return SMCParse_Continue; +} + +static InitializeUserParser() +{ + if (!g_hUserParser) + { + g_hUserParser = new SMCParser(); + g_hUserParser.OnEnterSection = ReadUsers_NewSection; + g_hUserParser.OnKeyValue = ReadUsers_KeyValue; + g_hUserParser.OnLeaveSection = ReadUsers_EndSection; + g_hUserParser.OnRawLine = ReadUsers_CurrentLine; + + g_GroupArray = CreateArray(); + } +} + +ReadUsers() +{ + InitializeUserParser(); + + BuildPath(Path_SM, g_Filename, sizeof(g_Filename), "configs/sourcebans/sb_admins.cfg"); + + /* Set states */ + InitGlobalStates(); + g_UserState = USER_STATE_NONE; + + SMCError err = g_hUserParser.ParseFile(g_Filename); + if (err != SMCError_Okay) + { + char buffer[64]; + if (g_hUserParser.GetErrorString(err, buffer, sizeof(buffer))) + { + ParseError("%s", buffer); + } + else + { + ParseError("Fatal parse error"); + } + } +} +/* SOURCEMOD 1.7 PLUGIN STOPS HERE */ +#else +enum UserState +{ + UserState_None, + UserState_Admins, + UserState_InAdmin, +} + +static SMCParser g_hUserParser; +static UserState g_UserState = UserState_None; +static char g_CurAuth[64]; +static char g_CurIdent[64]; +static char g_CurName[64]; +static char g_CurPass[64]; +static ArrayList g_GroupArray; +static int g_CurFlags; +static int g_CurImmunity; + +public SMCResult ReadUsers_NewSection(SMCParser smc, const char[] name, bool opt_quotes) +{ + if (g_IgnoreLevel) + { + g_IgnoreLevel++; + return SMCParse_Continue; + } + + if (g_UserState == UserState_None) + { + if (StrEqual(name, "Admins", false)) + { + g_UserState = UserState_Admins; + } + else + { + g_IgnoreLevel++; + } + } + else if (g_UserState == UserState_Admins) + { + g_UserState = UserState_InAdmin; + strcopy(g_CurName, sizeof(g_CurName), name); + g_CurAuth[0] = '\0'; + g_CurIdent[0] = '\0'; + g_CurPass[0] = '\0'; + g_GroupArray.Clear(); + g_CurFlags = 0; + g_CurImmunity = 0; + } + else + { + g_IgnoreLevel++; + } + + return SMCParse_Continue; +} + +public SMCResult ReadUsers_KeyValue(SMCParser smc, + const char[] key, + const char[] value, + bool key_quotes, + bool value_quotes) +{ + if (g_UserState != UserState_InAdmin || g_IgnoreLevel) + { + return SMCParse_Continue; + } + + if (StrEqual(key, "auth", false)) + { + strcopy(g_CurAuth, sizeof(g_CurAuth), value); + } + else if (StrEqual(key, "identity", false)) + { + strcopy(g_CurIdent, sizeof(g_CurIdent), value); + } + else if (StrEqual(key, "password", false)) + { + strcopy(g_CurPass, sizeof(g_CurPass), value); + } + else if (StrEqual(key, "group", false)) + { + GroupId id = FindAdmGroup(value); + if (id == INVALID_GROUP_ID) + { + ParseError("Unknown group \"%s\"", value); + } + + g_GroupArray.Push(id); + } + else if (StrEqual(key, "flags", false)) + { + int len = strlen(value); + AdminFlag flag; + + for (int i = 0; i < len; i++) + { + if (!FindFlagByChar(value[i], flag)) + { + ParseError("Invalid flag detected: %c", value[i]); + } + else + { + g_CurFlags |= FlagToBit(flag); + } + } + } + else if (StrEqual(key, "immunity", false)) + { + g_CurImmunity = StringToInt(value); + } + + return SMCParse_Continue; +} + +public SMCResult ReadUsers_EndSection(SMCParser smc) +{ + if (g_IgnoreLevel) + { + g_IgnoreLevel--; + return SMCParse_Continue; + } + + if (g_UserState == UserState_InAdmin) + { + /* Dump this user to memory */ + if (g_CurIdent[0] != '\0' && g_CurAuth[0] != '\0') + { + AdminFlag flags[26]; + AdminId id; + int i, num_groups, num_flags; + + if ((id = FindAdminByIdentity(g_CurAuth, g_CurIdent)) == INVALID_ADMIN_ID) + { + id = CreateAdmin(g_CurName); + if (!BindAdminIdentity(id, g_CurAuth, g_CurIdent)) + { + RemoveAdmin(id); + ParseError("Failed to bind auth \"%s\" to identity \"%s\"", g_CurAuth, g_CurIdent); + return SMCParse_Continue; + } + } + + num_groups = g_GroupArray.Length; + for (i = 0; i < num_groups; i++) + { + AdminInheritGroup(id, g_GroupArray.Get(i)); + } + + SetAdminPassword(id, g_CurPass); + if (GetAdminImmunityLevel(id) < g_CurImmunity) + { + SetAdminImmunityLevel(id, g_CurImmunity); + } + + num_flags = FlagBitsToArray(g_CurFlags, flags, sizeof(flags)); + for (i = 0; i < num_flags; i++) + { + SetAdminFlag(id, flags[i], true); + } + } + else + { + ParseError("Failed to create admin: did you forget either the auth or identity properties?"); + } + + g_UserState = UserState_Admins; + } + else if (g_UserState == UserState_Admins) + { + g_UserState = UserState_None; + } + + return SMCParse_Continue; +} + +public SMCResult ReadUsers_CurrentLine(SMCParser smc, const char[] line, int lineno) +{ + g_CurrentLine = lineno; + + return SMCParse_Continue; +} + +static void InitializeUserParser() +{ + if (!g_hUserParser) + { + g_hUserParser = new SMCParser(); + g_hUserParser.OnEnterSection = ReadUsers_NewSection; + g_hUserParser.OnKeyValue = ReadUsers_KeyValue; + g_hUserParser.OnLeaveSection = ReadUsers_EndSection; + g_hUserParser.OnRawLine = ReadUsers_CurrentLine; + + g_GroupArray = CreateArray(); + } +} + +void ReadUsers() +{ + InitializeUserParser(); + + BuildPath(Path_SM, g_Filename, sizeof(g_Filename), "configs/sourcebans/sb_admins.cfg"); + + /* Set states */ + InitGlobalStates(); + g_UserState = UserState_None; + + SMCError err = g_hUserParser.ParseFile(g_Filename); + if (err != SMCError_Okay) + { + char buffer[64]; + if (g_hUserParser.GetErrorString(err, buffer, sizeof(buffer))) + { + ParseError("%s", buffer); + } + else + { + ParseError("Fatal Parse Error"); + } + } +} +#endif \ No newline at end of file diff --git a/Sourcebans++/scripting/sbpp_checker.sp b/Sourcebans++/scripting/sbpp_checker.sp new file mode 100644 index 0000000..b0c70b4 --- /dev/null +++ b/Sourcebans++/scripting/sbpp_checker.sp @@ -0,0 +1,564 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 SourceBans++ Dev Team +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file is based off work(s) covered by the following copyright(s): +// +// SourceBans Checker 1.0.2 +// Copyright (C) 2010-2013 Nicholas Hastings +// Licensed under GNU GPL version 3, or later. +// Page: +// +// ************************************************************************* + +#include + +#define VERSION "1.6.4" +#define LISTBANS_USAGE "sm_listbans <#userid|name> - Lists a user's prior bans from Sourcebans" +#define LISTCOMMS_USAGE "sm_listcomms <#userid|name> - Lists a user's prior comms from Sourcebans" +#define INVALID_TARGET -1 + +new String:g_DatabasePrefix[10] = "sb"; +new Handle:g_ConfigParser; +new Handle:g_DB; + + + +public Plugin:myinfo = +{ + name = "SourceBans++: Bans Checker", + author = "psychonic, Ca$h Munny, SourceBans++ Dev Team", + description = "Notifies admins of prior bans from Sourcebans upon player connect.", + version = VERSION, + url = "https://sbpp.github.io" +}; + +public OnPluginStart() +{ + LoadTranslations("common.phrases"); + + CreateConVar("sbchecker_version", VERSION, "", FCVAR_NOTIFY); + RegAdminCmd("sm_listbans", OnListSourceBansCmd, ADMFLAG_BAN, LISTBANS_USAGE); + RegAdminCmd("sm_listcomms", OnListSourceCommsCmd, ADMFLAG_BAN, LISTCOMMS_USAGE); + RegAdminCmd("sb_reload", OnReloadCmd, ADMFLAG_RCON, "Reload sourcebans config and ban reason menu options"); + + SQL_TConnect(OnDatabaseConnected, "sourcebans"); +} + +public OnMapStart() +{ + ReadConfig(); +} + +public Action:OnReloadCmd(client, args) +{ + ReadConfig(); + return Plugin_Handled; +} + +public OnDatabaseConnected(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + if (hndl == INVALID_HANDLE) + SetFailState("Failed to connect to SourceBans DB, %s", error); + + g_DB = hndl; +} + +public OnClientAuthorized(client, const String:auth[]) +{ + if (g_DB == INVALID_HANDLE) + return; + /* Do not check bots nor check player with lan steamid. */ + if (auth[0] == 'B' || auth[9] == 'L') + return; + decl String:query[512], String:ip[30]; + GetClientIP(client, ip, sizeof(ip)); + FormatEx(query, sizeof(query), "SELECT COUNT(bid) FROM %s_bans WHERE ((type = 0 AND authid REGEXP '^STEAM_[0-9]:%s$') OR (type = 1 AND ip = '%s')) UNION SELECT COUNT(bid) FROM %s_comms WHERE authid REGEXP '^STEAM_[0-9]:%s$'", g_DatabasePrefix, auth[8], ip, g_DatabasePrefix,auth[8]); + SQL_TQuery(g_DB, OnConnectBanCheck, query, GetClientUserId(client), DBPrio_Low); +} +public OnConnectBanCheck(Handle:owner, Handle:hndl, const String:error[], any:userid) +{ + new client = GetClientOfUserId(userid); + if (!client || hndl == INVALID_HANDLE || !SQL_FetchRow(hndl)) + return; + new bancount = SQL_FetchInt(hndl, 0); + new commcount = 0; + if(SQL_FetchRow(hndl)){ + commcount = SQL_FetchInt(hndl, 0); + } + if (bancount > 0 || commcount > 0) + { + if(bancount == 0){ + PrintToBanAdmins("\x04[SourceBans++]\x01 Warning: Player \"%N\" has %d previous commban%s.", + client,commcount,((commcount > 1 || commcount == 0) ? "s":"")); + } + else if(commcount == 0){ + PrintToBanAdmins("\x04[SourceBans++]\x01 Warning: Player \"%N\" has %d previous ban%s.", + client, bancount, ((bancount > 1 || bancount == 0) ? "s":"")); + } + else{ + PrintToBanAdmins("\x04[SourceBans++]\x01 Warning: Player \"%N\" has %d previous ban%s and %d previous commban%s.", + client, bancount, ((bancount > 1 || bancount == 0) ? "s":""),commcount,((commcount > 1 || commcount == 0) ? "s":"")); + } + } +} + + + + + +public Action:OnListSourceBansCmd(client, args) +{ + if (args < 1) + { + ReplyToCommand(client, LISTBANS_USAGE); + } + + if (g_DB == INVALID_HANDLE) + { + ReplyToCommand(client, "Error: Database not ready."); + return Plugin_Handled; + } + + decl String:targetarg[64]; + GetCmdArg(1, targetarg, sizeof(targetarg)); + + new target = FindTarget(client, targetarg, true, true); + if (target == INVALID_TARGET) + { + ReplyToCommand(client, "Error: Could not find a target matching '%s'.", targetarg); + return Plugin_Handled; + } + + decl String:auth[32]; + if (!GetClientAuthId(target, AuthId_Steam2, auth, sizeof(auth)) + || auth[0] == 'B' || auth[9] == 'L') + { + ReplyToCommand(client, "Error: Could not retrieve %N's steam id.", target); + return Plugin_Handled; + } + + decl String:query[1024], String:ip[30]; + GetClientIP(target, ip, sizeof(ip)); + FormatEx(query, sizeof(query), "SELECT created, %s_admins.user, ends, length, reason, RemoveType FROM %s_bans LEFT JOIN %s_admins ON %s_bans.aid = %s_admins.aid WHERE ((type = 0 AND %s_bans.authid REGEXP '^STEAM_[0-9]:%s$') OR (type = 1 AND ip = '%s')) AND ((length > '0' AND ends > UNIX_TIMESTAMP()) OR RemoveType IS NOT NULL)", g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, auth[8], ip); + + decl String:targetName[MAX_NAME_LENGTH]; + GetClientName(target, targetName, sizeof(targetName)); + + new Handle:pack = CreateDataPack(); + WritePackCell(pack, (client == 0) ? 0 : GetClientUserId(client)); + WritePackString(pack, targetName); + + SQL_TQuery(g_DB, OnListBans, query, pack, DBPrio_Low); + + if (client == 0) + { + ReplyToCommand(client, "[SourceBans++] Note: if you are using this command through an rcon tool, you will not see results."); + } + else + { + ReplyToCommand(client, "\x04[SourceBans++]\x01 Look for %N's ban results in console.", target); + } + + return Plugin_Handled; +} + +public OnListBans(Handle:owner, Handle:hndl, const String:error[], any:pack) +{ + ResetPack(pack); + new clientuid = ReadPackCell(pack); + new client = GetClientOfUserId(clientuid); + decl String:targetName[MAX_NAME_LENGTH]; + ReadPackString(pack, targetName, sizeof(targetName)); + CloseHandle(pack); + + if (clientuid > 0 && client == 0) + return; + + if (hndl == INVALID_HANDLE) + { + PrintListResponse(clientuid, client, "[SourceBans++] DB error while retrieving bans for %s:\n%s", targetName, error); + return; + } + + if (SQL_GetRowCount(hndl) == 0) + { + PrintListResponse(clientuid, client, "[SourceBans++] No bans found for %s.", targetName); + return; + } + + PrintListResponse(clientuid, client, "[SourceBans++] Listing bans for %s", targetName); + PrintListResponse(clientuid, client, "Ban Date Banned By Length End Date R Reason"); + PrintListResponse(clientuid, client, "-------------------------------------------------------------------------------"); + while (SQL_FetchRow(hndl)) + { + new String:createddate[11] = " "; + new String:bannedby[11] = " "; + new String:lenstring[11] = "N/A "; + new String:enddate[11] = "N/A "; + new String:reason[28]; + new String:RemoveType[2] = " "; + + if (!SQL_IsFieldNull(hndl, 0)) + { + FormatTime(createddate, sizeof(createddate), "%Y-%m-%d", SQL_FetchInt(hndl, 0)); + } + + if (!SQL_IsFieldNull(hndl, 1)) + { + new size_bannedby = sizeof(bannedby); + SQL_FetchString(hndl, 1, bannedby, size_bannedby); + new len = SQL_FetchSize(hndl, 1); + if (len > size_bannedby - 1) + { + reason[size_bannedby - 4] = '.'; + reason[size_bannedby - 3] = '.'; + reason[size_bannedby - 2] = '.'; + } + else + { + for (new i = len; i < size_bannedby - 1; i++) + { + bannedby[i] = ' '; + } + } + } + + // NOT NULL + new size_lenstring = sizeof(lenstring); + new length = SQL_FetchInt(hndl, 3); + if (length == 0) + { + strcopy(lenstring, size_lenstring, "Permanent "); + } + else + { + new len = IntToString(length, lenstring, size_lenstring); + if (len < size_lenstring - 1) + { + // change the '\0' to a ' '. the original \0 at the end will still be there + lenstring[len] = ' '; + } + } + + if (!SQL_IsFieldNull(hndl, 2)) + { + FormatTime(enddate, sizeof(enddate), "%Y-%m-%d", SQL_FetchInt(hndl, 2)); + } + + // NOT NULL + new reason_size = sizeof(reason); + SQL_FetchString(hndl, 4, reason, reason_size); + new len = SQL_FetchSize(hndl, 4); + if (len > reason_size - 1) + { + reason[reason_size - 4] = '.'; + reason[reason_size - 3] = '.'; + reason[reason_size - 2] = '.'; + } + else + { + for (new i = len; i < reason_size - 1; i++) + { + reason[i] = ' '; + } + } + + if (!SQL_IsFieldNull(hndl, 5)) + { + SQL_FetchString(hndl, 5, RemoveType, sizeof(RemoveType)); + } + + PrintListResponse(clientuid, client, "%s %s %s %s %s %s", createddate, bannedby, lenstring, enddate, RemoveType, reason); + } +} + +public Action:OnListSourceCommsCmd(client, args) +{ + if (args < 1) + { + ReplyToCommand(client, LISTCOMMS_USAGE); + } + + if (g_DB == INVALID_HANDLE) + { + ReplyToCommand(client, "Error: Database not ready."); + return Plugin_Handled; + } + + decl String:targetarg[64]; + GetCmdArg(1, targetarg, sizeof(targetarg)); + + new target = FindTarget(client, targetarg, true, true); + if (target == INVALID_TARGET) + { + ReplyToCommand(client, "Error: Could not find a target matching '%s'.", targetarg); + return Plugin_Handled; + } + + decl String:auth[32]; + if (!GetClientAuthId(target, AuthId_Steam2, auth, sizeof(auth)) + || auth[0] == 'B' || auth[9] == 'L') + { + ReplyToCommand(client, "Error: Could not retrieve %N's steam id.", target); + return Plugin_Handled; + } + + decl String:query[1024]; + FormatEx(query, sizeof(query), "SELECT created, %s_admins.user, ends, length, reason, RemoveType, type FROM %s_comms LEFT JOIN %s_admins ON %s_comms.aid = %s_admins.aid WHERE %s_comms.authid REGEXP '^STEAM_[0-9]:%s$' AND ((length > '0' AND ends > UNIX_TIMESTAMP()) OR RemoveType IS NOT NULL)", g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, g_DatabasePrefix, auth[8]); + + decl String:targetName[MAX_NAME_LENGTH]; + GetClientName(target, targetName, sizeof(targetName)); + + new Handle:pack = CreateDataPack(); + WritePackCell(pack, (client == 0) ? 0 : GetClientUserId(client)); + WritePackString(pack, targetName); + + SQL_TQuery(g_DB, OnListComms, query, pack, DBPrio_Low); + + if (client == 0) + { + ReplyToCommand(client, "[SourceBans++] Note: if you are using this command through an rcon tool, you will not see results."); + } + else + { + ReplyToCommand(client, "\x04[SourceBans++]\x01 Look for %N's comm results in console.", target); + } + + return Plugin_Handled; +} + +public OnListComms(Handle:owner, Handle:hndl, const String:error[], any:pack) +{ + ResetPack(pack); + new clientuid = ReadPackCell(pack); + new client = GetClientOfUserId(clientuid); + decl String:targetName[MAX_NAME_LENGTH]; + ReadPackString(pack, targetName, sizeof(targetName)); + CloseHandle(pack); + + if (clientuid > 0 && client == 0) + return; + + if (hndl == INVALID_HANDLE) + { + PrintListResponse(clientuid, client, "[SourceBans++] DB error while retrieving comms for %s:\n%s", targetName, error); + return; + } + + if (SQL_GetRowCount(hndl) == 0) + { + PrintListResponse(clientuid, client, "[SourceBans++] No comms found for %s.", targetName); + return; + } + + + PrintListResponse(clientuid, client, "[SourceBans++] Listing comms for %s", targetName); + PrintListResponse(clientuid, client, "Ban Date Banned By Length End Date T R Reason"); + PrintListResponse(clientuid, client, "-------------------------------------------------------------------------------"); + while (SQL_FetchRow(hndl)) + { + new String:createddate[11] = " "; + new String:bannedby[11] = " "; + new String:lenstring[11] = "N/A "; + new String:enddate[11] = "N/A "; + new String:reason[23]; + new String:CommType[2] = " "; + new String:RemoveType[2] = " "; + + + + if (!SQL_IsFieldNull(hndl, 0)) + { + FormatTime(createddate, sizeof(createddate), "%Y-%m-%d", SQL_FetchInt(hndl, 0)); + } + + if (!SQL_IsFieldNull(hndl, 1)) + { + new size_bannedby = sizeof(bannedby); + SQL_FetchString(hndl, 1, bannedby, size_bannedby); + new len = SQL_FetchSize(hndl, 1); + if (len > size_bannedby - 1) + { + reason[size_bannedby - 4] = '.'; + reason[size_bannedby - 3] = '.'; + reason[size_bannedby - 2] = '.'; + } + else + { + for (new i = len; i < size_bannedby - 1; i++) + { + bannedby[i] = ' '; + } + } + } + + // NOT NULL + new size_lenstring = sizeof(lenstring); + new length = SQL_FetchInt(hndl, 3); + if (length == 0) + { + strcopy(lenstring, size_lenstring, "Permanent "); + } + else + { + new len = IntToString(length, lenstring, size_lenstring); + if (len < size_lenstring - 1) + { + // change the '\0' to a ' '. the original \0 at the end will still be there + lenstring[len] = ' '; + } + } + + if (!SQL_IsFieldNull(hndl, 2)) + { + FormatTime(enddate, sizeof(enddate), "%Y-%m-%d", SQL_FetchInt(hndl, 2)); + } + + // NOT NULL + new reason_size = sizeof(reason); + SQL_FetchString(hndl, 4, reason, reason_size); + new len = SQL_FetchSize(hndl, 4); + if (len > reason_size - 1) + { + reason[reason_size - 4] = '.'; + reason[reason_size - 3] = '.'; + reason[reason_size - 2] = '.'; + } + else + { + for (new i = len; i < reason_size - 1; i++) + { + reason[i] = ' '; + } + } + + if (!SQL_IsFieldNull(hndl, 5)) + { + SQL_FetchString(hndl, 5, RemoveType, sizeof(RemoveType)); + } + // NOT NULL + SQL_FetchString(hndl, 6, CommType, sizeof(RemoveType)); + if(StrEqual(CommType,"1")) + strcopy(CommType, sizeof(CommType), "M"); + if(StrEqual(CommType,"2")) + strcopy(CommType, sizeof(CommType), "G"); + + PrintListResponse(clientuid, client, "%s %s %s %s %s %s %s", createddate, bannedby, lenstring, enddate, CommType, RemoveType, reason); + + + } +} + +PrintListResponse(userid, client, const String:format[], any:...) +{ + decl String:msg[192]; + VFormat(msg, sizeof(msg), format, 4); + + if (userid == 0) + { + PrintToServer("%s", msg); + } + else + { + PrintToConsole(client, "%s", msg); + } +} + +PrintToBanAdmins(const String:format[], any:...) +{ + decl String:msg[128]; + VFormat(msg, sizeof(msg), format, 2); + + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i) + && CheckCommandAccess(i, "sm_listsourcebans", ADMFLAG_BAN) + ) + { + PrintToChat(i, "%s", msg); + } + } +} + +stock ReadConfig() +{ + InitializeConfigParser(); + + if (g_ConfigParser == INVALID_HANDLE) + { + return; + } + + decl String:ConfigFile[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, ConfigFile, sizeof(ConfigFile), "configs/sourcebans/sourcebans.cfg"); + + if (FileExists(ConfigFile)) + { + InternalReadConfig(ConfigFile); + } + else + { + decl String:Error[PLATFORM_MAX_PATH + 64]; + FormatEx(Error, sizeof(Error), "FATAL *** ERROR *** can not find %s", ConfigFile); + SetFailState(Error); + } +} + +static InitializeConfigParser() +{ + if (g_ConfigParser == INVALID_HANDLE) + { + g_ConfigParser = SMC_CreateParser(); + SMC_SetReaders(g_ConfigParser, ReadConfig_NewSection, ReadConfig_KeyValue, ReadConfig_EndSection); + } +} + +static InternalReadConfig(const String:path[]) +{ + new SMCError:err = SMC_ParseFile(g_ConfigParser, path); + + if (err != SMCError_Okay) + { + decl String:buffer[64]; + PrintToServer("%s", SMC_GetErrorString(err, buffer, sizeof(buffer)) ? buffer : "Fatal parse error"); + } +} + +public SMCResult:ReadConfig_NewSection(Handle:smc, const String:name[], bool:opt_quotes) +{ + return SMCParse_Continue; +} + +public SMCResult:ReadConfig_KeyValue(Handle:smc, const String:key[], const String:value[], bool:key_quotes, bool:value_quotes) +{ + if (strcmp("DatabasePrefix", key, false) == 0) + { + strcopy(g_DatabasePrefix, sizeof(g_DatabasePrefix), value); + + if (g_DatabasePrefix[0] == '\0') + { + g_DatabasePrefix = "sb"; + } + } + + return SMCParse_Continue; +} + +public SMCResult:ReadConfig_EndSection(Handle:smc) +{ + return SMCParse_Continue; +} diff --git a/Sourcebans++/scripting/sbpp_comms.sp b/Sourcebans++/scripting/sbpp_comms.sp new file mode 100644 index 0000000..57f5e3e --- /dev/null +++ b/Sourcebans++/scripting/sbpp_comms.sp @@ -0,0 +1,3279 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 SourceBans++ Dev Team +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file is based off work(s) covered by the following copyright(s): +// +// SourceComms 0.9.266 +// Copyright (C) 2013-2014 Alexandr Duplishchev +// Licensed under GNU GPL version 3, or later. +// Page: - +// +// ************************************************************************* + +#pragma semicolon 1 + +#include +#include +#include + +#undef REQUIRE_PLUGIN +#include + +#define UNBLOCK_FLAG ADMFLAG_CHEATS +#define DATABASE "sourcebans" + +// #define DEBUG +// #define LOG_QUERIES + +// Do not edit below this line // +//-----------------------------// + +#define PLUGIN_VERSION "1.6.4" +#define PREFIX "\x04[SourceComms++]\x01 " + +#define MAX_TIME_MULTI 30 // maximum mass-target punishment length +// session mute will expire after this if it hasn't already (fallback) +#define SESSION_MUTE_FALLBACK 120 * 60 + +#define NOW 0 +#define TYPE_TEMP_SHIFT 10 + +#define MAX_REASONS 32 +#define DISPLAY_SIZE 64 +#define REASON_SIZE 192 + +new iNumReasons; +new String:g_sReasonDisplays[MAX_REASONS][DISPLAY_SIZE], String:g_sReasonKey[MAX_REASONS][REASON_SIZE]; + +#define MAX_TIMES 32 +new iNumTimes, g_iTimeMinutes[MAX_TIMES]; +new String:g_sTimeDisplays[MAX_TIMES][DISPLAY_SIZE]; + +enum State/* ConfigState */ +{ + ConfigStateNone = 0, + ConfigStateConfig, + ConfigStateReasons, + ConfigStateTimes, + ConfigStateServers, +} +enum DatabaseState/* Database connection state */ +{ + DatabaseState_None = 0, + DatabaseState_Wait, + DatabaseState_Connecting, + DatabaseState_Connected, +} + +new DatabaseState:g_DatabaseState; +new g_iConnectLock = 0; +new g_iSequence = 0; + +new State:ConfigState; +new Handle:ConfigParser; + +new Handle:hTopMenu = INVALID_HANDLE; + +/* Cvar handle*/ +new Handle:CvarHostIp; +new Handle:CvarPort; + +new String:ServerIp[24]; +new String:ServerPort[7]; + +/* Database handle */ +new Handle:g_hDatabase; +new Handle:SQLiteDB; + +new String:DatabasePrefix[10] = "sb"; + +/* Timer handles */ +new Handle:g_hPlayerRecheck[MAXPLAYERS + 1] = { INVALID_HANDLE, ... }; +new Handle:g_hGagExpireTimer[MAXPLAYERS + 1] = { INVALID_HANDLE, ... }; +new Handle:g_hMuteExpireTimer[MAXPLAYERS + 1] = { INVALID_HANDLE, ... }; + + +/* Log Stuff */ +#if defined LOG_QUERIES +new String:logQuery[256]; +#endif + +new Float:RetryTime = 15.0; +new DefaultTime = 30; +new DisUBImCheck = 0; +new ConsoleImmunity = 0; +new ConfigMaxLength = 0; +new ConfigWhiteListOnly = 0; +new serverID = 0; + +/* List menu */ +enum PeskyPanels +{ + curTarget, + curIndex, + viewingMute, + viewingGag, + viewingList, +} +new g_iPeskyPanels[MAXPLAYERS + 1][PeskyPanels]; + +new bool:g_bPlayerStatus[MAXPLAYERS + 1]; // Player block check status +new String:g_sName[MAXPLAYERS + 1][MAX_NAME_LENGTH]; + +new bType:g_MuteType[MAXPLAYERS + 1]; +new g_iMuteTime[MAXPLAYERS + 1]; +new g_iMuteLength[MAXPLAYERS + 1]; // in sec +new g_iMuteLevel[MAXPLAYERS + 1]; // immunity level of admin +new String:g_sMuteAdminName[MAXPLAYERS + 1][MAX_NAME_LENGTH]; +new String:g_sMuteReason[MAXPLAYERS + 1][256]; +new String:g_sMuteAdminAuth[MAXPLAYERS + 1][64]; + +new bType:g_GagType[MAXPLAYERS + 1]; +new g_iGagTime[MAXPLAYERS + 1]; +new g_iGagLength[MAXPLAYERS + 1]; // in sec +new g_iGagLevel[MAXPLAYERS + 1]; // immunity level of admin +new String:g_sGagAdminName[MAXPLAYERS + 1][MAX_NAME_LENGTH]; +new String:g_sGagReason[MAXPLAYERS + 1][256]; +new String:g_sGagAdminAuth[MAXPLAYERS + 1][64]; + +new Handle:g_hServersWhiteList = INVALID_HANDLE; + +// Forward +new Handle:g_hFwd_OnPlayerPunished; + +public Plugin:myinfo = +{ + name = "SourceBans++: SourceComms", + author = "Alex, SourceBans++ Dev Team", + description = "Advanced punishments management for the Source engine in SourceBans style", + version = PLUGIN_VERSION, + url = "https://sbpp.github.io" +}; + +public APLRes:AskPluginLoad2(Handle:myself, bool:late, String:error[], err_max) +{ + CreateNative("SourceComms_SetClientMute", Native_SetClientMute); + CreateNative("SourceComms_SetClientGag", Native_SetClientGag); + CreateNative("SourceComms_GetClientMuteType", Native_GetClientMuteType); + CreateNative("SourceComms_GetClientGagType", Native_GetClientGagType); + + g_hFwd_OnPlayerPunished = CreateGlobalForward("SourceComms_OnBlockAdded", ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell, Param_String); + + MarkNativeAsOptional("SQL_SetCharset"); + RegPluginLibrary("sourcecomms++"); + return APLRes_Success; +} + +public OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("sourcecomms.phrases"); + + new Handle:hTemp = INVALID_HANDLE; + if (LibraryExists("adminmenu") && ((hTemp = GetAdminTopMenu()) != INVALID_HANDLE)) + OnAdminMenuReady(hTemp); + + CvarHostIp = FindConVar("hostip"); + CvarPort = FindConVar("hostport"); + g_hServersWhiteList = CreateArray(); + + CreateConVar("sourcecomms_version", PLUGIN_VERSION, _, FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY); + AddCommandListener(CommandCallback, "sm_gag"); + AddCommandListener(CommandCallback, "sm_mute"); + AddCommandListener(CommandCallback, "sm_silence"); + AddCommandListener(CommandCallback, "sm_ungag"); + AddCommandListener(CommandCallback, "sm_unmute"); + AddCommandListener(CommandCallback, "sm_unsilence"); + RegServerCmd("sc_fw_block", FWBlock, "Blocking player comms by command from sourceban web site"); + RegServerCmd("sc_fw_ungag", FWUngag, "Ungagging player by command from sourceban web site"); + RegServerCmd("sc_fw_unmute", FWUnmute, "Unmuting player by command from sourceban web site"); + RegConsoleCmd("sm_comms", CommandComms, "Shows current player communications status"); + + HookEvent("player_changename", Event_OnPlayerName, EventHookMode_Post); + + #if defined LOG_QUERIES + BuildPath(Path_SM, logQuery, sizeof(logQuery), "logs/sourcecomms-q.log"); + #endif + + #if defined DEBUG + PrintToServer("Sourcecomms plugin loading. Version %s", PLUGIN_VERSION); + #endif + + // Catch config error + if (!SQL_CheckConfig(DATABASE)) + { + SetFailState("Database failure: could not find database config: %s", DATABASE); + return; + } + DB_Connect(); + InitializeBackupDB(); + + ServerInfo(); + + for (new client = 1; client <= MaxClients; client++) + { + if (IsClientInGame(client) && IsClientAuthorized(client)) + OnClientPostAdminCheck(client); + } +} + +public OnLibraryRemoved(const String:name[]) +{ + if (StrEqual(name, "adminmenu")) + hTopMenu = INVALID_HANDLE; +} + +public OnMapStart() +{ + ReadConfig(); +} + +public OnMapEnd() +{ + // Clean up on map end just so we can start a fresh connection when we need it later. + // Also it is necessary for using SQL_SetCharset + if (g_hDatabase) + CloseHandle(g_hDatabase); + + g_hDatabase = INVALID_HANDLE; +} + + +// CLIENT CONNECTION FUNCTIONS // + +public OnClientDisconnect(client) +{ + if (g_hPlayerRecheck[client] != INVALID_HANDLE && CloseHandle(g_hPlayerRecheck[client])) + g_hPlayerRecheck[client] = INVALID_HANDLE; + + CloseMuteExpireTimer(client); + CloseGagExpireTimer(client); +} + +public bool:OnClientConnect(client, String:rejectmsg[], maxlen) +{ + g_bPlayerStatus[client] = false; + return true; +} + +public OnClientConnected(client) +{ + g_sName[client][0] = '\0'; + + MarkClientAsUnMuted(client); + MarkClientAsUnGagged(client); +} + +public OnClientPostAdminCheck(client) +{ + decl String:clientAuth[64]; + GetClientAuthId(client, AuthId_Steam2, clientAuth, sizeof(clientAuth)); + GetClientName(client, g_sName[client], sizeof(g_sName[])); + + /* Do not check bots or check player with lan steamid. */ + if (clientAuth[0] == 'B' || clientAuth[9] == 'L' || !DB_Connect()) + { + g_bPlayerStatus[client] = true; + return; + } + + if (client > 0 && IsClientInGame(client) && !IsFakeClient(client)) + { + // if plugin was late loaded + if (BaseComm_IsClientMuted(client)) + { + MarkClientAsMuted(client); + } + if (BaseComm_IsClientGagged(client)) + { + MarkClientAsGagged(client); + } + + decl String:sClAuthYZEscaped[sizeof(clientAuth) * 2 + 1]; + SQL_EscapeString(g_hDatabase, clientAuth[8], sClAuthYZEscaped, sizeof(sClAuthYZEscaped)); + + decl String:Query[4096]; + FormatEx(Query, sizeof(Query), + "SELECT (c.ends - UNIX_TIMESTAMP()) AS remaining, \ + c.length, c.type, c.created, c.reason, a.user, \ + IF (a.immunity>=g.immunity, a.immunity, IFNULL(g.immunity,0)) AS immunity, \ + c.aid, c.sid, a.authid \ + FROM %s_comms AS c \ + LEFT JOIN %s_admins AS a ON a.aid = c.aid \ + LEFT JOIN %s_srvgroups AS g ON g.name = a.srv_group \ + WHERE RemoveType IS NULL \ + AND c.authid REGEXP '^STEAM_[0-9]:%s$' \ + AND (length = '0' OR ends > UNIX_TIMESTAMP())", + DatabasePrefix, DatabasePrefix, DatabasePrefix, sClAuthYZEscaped); + #if defined LOG_QUERIES + LogToFile(logQuery, "OnClientPostAdminCheck for: %s. QUERY: %s", clientAuth, Query); + #endif + SQL_TQuery(g_hDatabase, Query_VerifyBlock, Query, GetClientUserId(client), DBPrio_High); + } +} + + +// OTHER CLIENT CODE // + +public Action:Event_OnPlayerName(Handle:event, const String:name[], bool:dontBroadcast) +{ + new client = GetClientOfUserId(GetEventInt(event, "userid")); + if (client > 0 && IsClientInGame(client)) + GetEventString(event, "newname", g_sName[client], sizeof(g_sName[])); +} + +public BaseComm_OnClientMute(client, bool:muteState) +{ + if (client > 0 && client <= MaxClients) + { + if (muteState) + { + if (g_MuteType[client] == bNot) + { + MarkClientAsMuted(client, _, _, _, _, _, "Muted through BaseComm natives"); + SavePunishment(_, client, TYPE_MUTE, _, "Muted through BaseComm natives"); + } + } + else + { + if (g_MuteType[client] > bNot) + { + MarkClientAsUnMuted(client); + } + } + } +} + +public BaseComm_OnClientGag(client, bool:gagState) +{ + if (client > 0 && client <= MaxClients) + { + if (gagState) + { + if (g_GagType[client] == bNot) + { + MarkClientAsGagged(client, _, _, _, _, _, "Gagged through BaseComm natives"); + SavePunishment(_, client, TYPE_GAG, _, "Gagged through BaseComm natives"); + } + } + else + { + if (g_GagType[client] > bNot) + { + MarkClientAsUnGagged(client); + } + } + } +} + +// COMMAND CODE // + +public Action:CommandComms(client, args) +{ + if (!client) + { + ReplyToCommand(client, "%s%t", PREFIX, "CommandComms_na"); + return Plugin_Continue; + } + + if (g_MuteType[client] > bNot || g_GagType[client] > bNot) + AdminMenu_ListTarget(client, client, 0); + else + ReplyToCommand(client, "%s%t", PREFIX, "CommandComms_nb"); + + return Plugin_Handled; +} + +public Action:FWBlock(args) +{ + decl String:arg_string[256]; + new String:sArg[3][64]; + GetCmdArgString(arg_string, sizeof(arg_string)); + + decl type, length; + if (ExplodeString(arg_string, " ", sArg, 3, 64) != 3 || !StringToIntEx(sArg[0], type) || type < 1 || type > 3 || !StringToIntEx(sArg[1], length)) + { + LogError("Wrong usage of sc_fw_block"); + return Plugin_Stop; + } + + LogMessage("Received block command from web: steam %s, type %d, length %d", sArg[2], type, length); + + decl String:clientAuth[64]; + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && IsClientAuthorized(i) && !IsFakeClient(i)) + { + GetClientAuthId(i, AuthId_Steam2, clientAuth, sizeof(clientAuth)); + if (strcmp(clientAuth, sArg[2], false) == 0) + { + #if defined DEBUG + PrintToServer("Catched %s for blocking from web", clientAuth); + #endif + + switch (type) { + case TYPE_MUTE:setMute(i, length, clientAuth); + case TYPE_GAG:setGag(i, length, clientAuth); + case TYPE_SILENCE: { setMute(i, length, clientAuth); setGag(i, length, clientAuth); } + } + break; + } + } + } + + return Plugin_Handled; +} + +public Action:FWUngag(args) +{ + decl String:arg_string[256]; + new String:sArg[1][64]; + GetCmdArgString(arg_string, sizeof(arg_string)); + if (!ExplodeString(arg_string, " ", sArg, 1, 64)) + { + LogError("Wrong usage of sc_fw_ungag"); + return Plugin_Stop; + } + + LogMessage("Received ungag command from web: steam %s", sArg[0]); + + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && IsClientAuthorized(i) && !IsFakeClient(i)) + { + decl String:clientAuth[64]; + GetClientAuthId(i, AuthId_Steam2, clientAuth, sizeof(clientAuth)); + if (strcmp(clientAuth, sArg[0], false) == 0) + { + #if defined DEBUG + PrintToServer("Catched %s for ungagging from web", clientAuth); + #endif + + if (g_GagType[i] > bNot) + { + PerformUnGag(i); + PrintToChat(i, "%s%t", PREFIX, "FWUngag"); + LogMessage("%s is ungagged from web", clientAuth); + } + else + LogError("Can't ungag %s from web, it isn't gagged", clientAuth); + break; + } + } + } + return Plugin_Handled; +} + +public Action:FWUnmute(args) +{ + decl String:arg_string[256]; + new String:sArg[1][64]; + GetCmdArgString(arg_string, sizeof(arg_string)); + if (!ExplodeString(arg_string, " ", sArg, 1, 64)) + { + LogError("Wrong usage of sc_fw_ungag"); + return Plugin_Stop; + } + + LogMessage("Received unmute command from web: steam %s", sArg[0]); + + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && IsClientAuthorized(i) && !IsFakeClient(i)) + { + decl String:clientAuth[64]; + GetClientAuthId(i, AuthId_Steam2, clientAuth, sizeof(clientAuth)); + if (strcmp(clientAuth, sArg[0], false) == 0) + { + #if defined DEBUG + PrintToServer("Catched %s for unmuting from web", clientAuth); + #endif + + if (g_MuteType[i] > bNot) + { + PerformUnMute(i); + PrintToChat(i, "%s%t", PREFIX, "FWUnmute"); + LogMessage("%s is unmuted from web", clientAuth); + } + else + LogError("Can't unmute %s from web, it isn't muted", clientAuth); + break; + } + } + } + return Plugin_Handled; +} + + +public Action:CommandCallback(client, const String:command[], args) +{ + if (client && !CheckCommandAccess(client, command, ADMFLAG_CHAT)) + return Plugin_Continue; + + new type; + if (StrEqual(command, "sm_gag", false)) + type = TYPE_GAG; + else if (StrEqual(command, "sm_mute", false)) + type = TYPE_MUTE; + else if (StrEqual(command, "sm_ungag", false)) + type = TYPE_UNGAG; + else if (StrEqual(command, "sm_unmute", false)) + type = TYPE_UNMUTE; + else if (StrEqual(command, "sm_silence", false)) + type = TYPE_SILENCE; + else if (StrEqual(command, "sm_unsilence", false)) + type = TYPE_UNSILENCE; + else + return Plugin_Stop; + + if (args < 1) + { + ReplyToCommand(client, "%sUsage: %s <#userid|name> %s", PREFIX, command, type <= TYPE_SILENCE ? "[time|0] [reason]" : "[reason]"); + if (type <= TYPE_SILENCE) + ReplyToCommand(client, "%sUsage: %s <#userid|name> [reason]", PREFIX, command); + return Plugin_Stop; + } + + decl String:sBuffer[256]; + GetCmdArgString(sBuffer, sizeof(sBuffer)); + + if (type <= TYPE_SILENCE) + CreateBlock(client, _, _, type, _, sBuffer); + else + ProcessUnBlock(client, _, type, _, sBuffer); + + return Plugin_Stop; +} + + +// MENU CODE // + +public OnAdminMenuReady(Handle:topmenu) +{ + /* Block us from being called twice */ + if (topmenu == hTopMenu) + return; + + /* Save the Handle */ + hTopMenu = topmenu; + + new TopMenuObject:MenuObject = AddToTopMenu(hTopMenu, "sourcecomm_cmds", TopMenuObject_Category, Handle_Commands, INVALID_TOPMENUOBJECT); + if (MenuObject == INVALID_TOPMENUOBJECT) + return; + + AddToTopMenu(hTopMenu, "sourcecomm_gag", TopMenuObject_Item, Handle_MenuGag, MenuObject, "sm_gag", ADMFLAG_CHAT); + AddToTopMenu(hTopMenu, "sourcecomm_ungag", TopMenuObject_Item, Handle_MenuUnGag, MenuObject, "sm_ungag", ADMFLAG_CHAT); + AddToTopMenu(hTopMenu, "sourcecomm_mute", TopMenuObject_Item, Handle_MenuMute, MenuObject, "sm_mute", ADMFLAG_CHAT); + AddToTopMenu(hTopMenu, "sourcecomm_unmute", TopMenuObject_Item, Handle_MenuUnMute, MenuObject, "sm_unmute", ADMFLAG_CHAT); + AddToTopMenu(hTopMenu, "sourcecomm_silence", TopMenuObject_Item, Handle_MenuSilence, MenuObject, "sm_silence", ADMFLAG_CHAT); + AddToTopMenu(hTopMenu, "sourcecomm_unsilence", TopMenuObject_Item, Handle_MenuUnSilence, MenuObject, "sm_unsilence", ADMFLAG_CHAT); + AddToTopMenu(hTopMenu, "sourcecomm_list", TopMenuObject_Item, Handle_MenuList, MenuObject, "sm_commlist", ADMFLAG_CHAT); +} + +public Handle_Commands(Handle:menu, TopMenuAction:action, TopMenuObject:object_id, param1, String:buffer[], maxlength) +{ + switch (action) + { + case TopMenuAction_DisplayOption: + Format(buffer, maxlength, "%T", "AdminMenu_Main", param1); + case TopMenuAction_DisplayTitle: + Format(buffer, maxlength, "%T", "AdminMenu_Select_Main", param1); + } +} + +public Handle_MenuGag(Handle:menu, TopMenuAction:action, TopMenuObject:object_id, param1, String:buffer[], maxlength) +{ + if (action == TopMenuAction_DisplayOption) + Format(buffer, maxlength, "%T", "AdminMenu_Gag", param1); + else if (action == TopMenuAction_SelectOption) + AdminMenu_Target(param1, TYPE_GAG); +} + +public Handle_MenuUnGag(Handle:menu, TopMenuAction:action, TopMenuObject:object_id, param1, String:buffer[], maxlength) +{ + if (action == TopMenuAction_DisplayOption) + Format(buffer, maxlength, "%T", "AdminMenu_UnGag", param1); + else if (action == TopMenuAction_SelectOption) + AdminMenu_Target(param1, TYPE_UNGAG); +} + +public Handle_MenuMute(Handle:menu, TopMenuAction:action, TopMenuObject:object_id, param1, String:buffer[], maxlength) +{ + if (action == TopMenuAction_DisplayOption) + Format(buffer, maxlength, "%T", "AdminMenu_Mute", param1); + else if (action == TopMenuAction_SelectOption) + AdminMenu_Target(param1, TYPE_MUTE); +} + +public Handle_MenuUnMute(Handle:menu, TopMenuAction:action, TopMenuObject:object_id, param1, String:buffer[], maxlength) +{ + if (action == TopMenuAction_DisplayOption) + Format(buffer, maxlength, "%T", "AdminMenu_UnMute", param1); + else if (action == TopMenuAction_SelectOption) + AdminMenu_Target(param1, TYPE_UNMUTE); +} + +public Handle_MenuSilence(Handle:menu, TopMenuAction:action, TopMenuObject:object_id, param1, String:buffer[], maxlength) +{ + if (action == TopMenuAction_DisplayOption) + Format(buffer, maxlength, "%T", "AdminMenu_Silence", param1); + else if (action == TopMenuAction_SelectOption) + AdminMenu_Target(param1, TYPE_SILENCE); +} + +public Handle_MenuUnSilence(Handle:menu, TopMenuAction:action, TopMenuObject:object_id, param1, String:buffer[], maxlength) +{ + if (action == TopMenuAction_DisplayOption) + Format(buffer, maxlength, "%T", "AdminMenu_UnSilence", param1); + else if (action == TopMenuAction_SelectOption) + AdminMenu_Target(param1, TYPE_UNSILENCE); +} + +public Handle_MenuList(Handle:menu, TopMenuAction:action, TopMenuObject:object_id, param1, String:buffer[], maxlength) +{ + if (action == TopMenuAction_DisplayOption) + Format(buffer, maxlength, "%T", "AdminMenu_List", param1); + else if (action == TopMenuAction_SelectOption) + { + g_iPeskyPanels[param1][viewingList] = false; + AdminMenu_List(param1, 0); + } +} + +AdminMenu_Target(client, type) +{ + decl String:Title[192], String:Option[32]; + switch (type) + { + case TYPE_GAG: + Format(Title, sizeof(Title), "%T", "AdminMenu_Select_Gag", client); + case TYPE_MUTE: + Format(Title, sizeof(Title), "%T", "AdminMenu_Select_Mute", client); + case TYPE_SILENCE: + Format(Title, sizeof(Title), "%T", "AdminMenu_Select_Silence", client); + case TYPE_UNGAG: + Format(Title, sizeof(Title), "%T", "AdminMenu_Select_Ungag", client); + case TYPE_UNMUTE: + Format(Title, sizeof(Title), "%T", "AdminMenu_Select_Unmute", client); + case TYPE_UNSILENCE: + Format(Title, sizeof(Title), "%T", "AdminMenu_Select_Unsilence", client); + } + + new Handle:hMenu = CreateMenu(MenuHandler_MenuTarget); // Common menu - players list. Almost full for blocking, and almost empty for unblocking + SetMenuTitle(hMenu, Title); + SetMenuExitBackButton(hMenu, true); + + new iClients; + if (type <= 3) // Mute, gag, silence + { + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i)) + { + switch (type) + { + case TYPE_MUTE: + if (g_MuteType[i] > bNot) + continue; + case TYPE_GAG: + if (g_GagType[i] > bNot) + continue; + case TYPE_SILENCE: + if (g_MuteType[i] > bNot || g_GagType[i] > bNot) + continue; + } + iClients++; + strcopy(Title, sizeof(Title), g_sName[i]); + AdminMenu_GetPunishPhrase(client, i, Title, sizeof(Title)); + Format(Option, sizeof(Option), "%d %d", GetClientUserId(i), type); + AddMenuItem(hMenu, Option, Title, (CanUserTarget(client, i) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED)); + } + } + } + else // UnMute, ungag, unsilence + { + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i)) + { + switch (type) + { + case TYPE_UNMUTE: + { + if (g_MuteType[i] > bNot) + { + iClients++; + strcopy(Title, sizeof(Title), g_sName[i]); + Format(Option, sizeof(Option), "%d %d", GetClientUserId(i), type); + AddMenuItem(hMenu, Option, Title, (CanUserTarget(client, i) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED)); + } + } + case TYPE_UNGAG: + { + if (g_GagType[i] > bNot) + { + iClients++; + strcopy(Title, sizeof(Title), g_sName[i]); + Format(Option, sizeof(Option), "%d %d", GetClientUserId(i), type); + AddMenuItem(hMenu, Option, Title, (CanUserTarget(client, i) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED)); + } + } + case TYPE_UNSILENCE: + { + if (g_MuteType[i] > bNot && g_GagType[i] > bNot) + { + iClients++; + strcopy(Title, sizeof(Title), g_sName[i]); + Format(Option, sizeof(Option), "%d %d", GetClientUserId(i), type); + AddMenuItem(hMenu, Option, Title, (CanUserTarget(client, i) ? ITEMDRAW_DEFAULT : ITEMDRAW_DISABLED)); + } + } + } + } + } + } + if (!iClients) + { + switch (type) + { + case TYPE_UNMUTE: + Format(Title, sizeof(Title), "%T", "AdminMenu_Option_Mute_Empty", client); + case TYPE_UNGAG: + Format(Title, sizeof(Title), "%T", "AdminMenu_Option_Gag_Empty", client); + case TYPE_UNSILENCE: + Format(Title, sizeof(Title), "%T", "AdminMenu_Option_Silence_Empty", client); + default: + Format(Title, sizeof(Title), "%T", "AdminMenu_Option_Empty", client); + } + AddMenuItem(hMenu, "0", Title, ITEMDRAW_DISABLED); + } + + DisplayMenu(hMenu, client, MENU_TIME_FOREVER); +} + +public MenuHandler_MenuTarget(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + CloseHandle(menu); + case MenuAction_Cancel: + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + case MenuAction_Select: + { + decl String:Option[32], String:Temp[2][8]; + GetMenuItem(menu, param2, Option, sizeof(Option)); + ExplodeString(Option, " ", Temp, 2, 8); + new target = GetClientOfUserId(StringToInt(Temp[0])); + + if (Bool_ValidMenuTarget(param1, target)) + { + new type = StringToInt(Temp[1]); + if (type <= TYPE_SILENCE) + AdminMenu_Duration(param1, target, type); + else + ProcessUnBlock(param1, target, type); + } + } + } +} + +AdminMenu_Duration(client, target, type) +{ + new Handle:hMenu = CreateMenu(MenuHandler_MenuDuration); + decl String:sBuffer[192], String:sTemp[64]; + Format(sBuffer, sizeof(sBuffer), "%T", "AdminMenu_Title_Durations", client); + SetMenuTitle(hMenu, sBuffer); + SetMenuExitBackButton(hMenu, true); + + for (new i = 0; i <= iNumTimes; i++) + { + if (IsAllowedBlockLength(client, g_iTimeMinutes[i])) + { + Format(sTemp, sizeof(sTemp), "%d %d %d", GetClientUserId(target), type, i); // TargetID TYPE_BLOCK index_of_Time + AddMenuItem(hMenu, sTemp, g_sTimeDisplays[i]); + } + } + + DisplayMenu(hMenu, client, MENU_TIME_FOREVER); +} + +public MenuHandler_MenuDuration(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + CloseHandle(menu); + case MenuAction_Cancel: + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + case MenuAction_Select: + { + decl String:sOption[32], String:sTemp[3][8]; + GetMenuItem(menu, param2, sOption, sizeof(sOption)); + ExplodeString(sOption, " ", sTemp, 3, 8); + // TargetID TYPE_BLOCK index_of_Time + new target = GetClientOfUserId(StringToInt(sTemp[0])); + + if (Bool_ValidMenuTarget(param1, target)) + { + new type = StringToInt(sTemp[1]); + new lengthIndex = StringToInt(sTemp[2]); + + if (iNumReasons) // we have reasons to show + AdminMenu_Reason(param1, target, type, lengthIndex); + else + CreateBlock(param1, target, g_iTimeMinutes[lengthIndex], type); + } + } + } +} + +AdminMenu_Reason(client, target, type, lengthIndex) +{ + new Handle:hMenu = CreateMenu(MenuHandler_MenuReason); + decl String:sBuffer[192], String:sTemp[64]; + Format(sBuffer, sizeof(sBuffer), "%T", "AdminMenu_Title_Reasons", client); + SetMenuTitle(hMenu, sBuffer); + SetMenuExitBackButton(hMenu, true); + + for (new i = 0; i <= iNumReasons; i++) + { + Format(sTemp, sizeof(sTemp), "%d %d %d %d", GetClientUserId(target), type, i, lengthIndex); // TargetID TYPE_BLOCK ReasonIndex LenghtIndex + AddMenuItem(hMenu, sTemp, g_sReasonDisplays[i]); + } + + DisplayMenu(hMenu, client, MENU_TIME_FOREVER); +} + +public MenuHandler_MenuReason(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + CloseHandle(menu); + case MenuAction_Cancel: + { + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + case MenuAction_Select: + { + decl String:sOption[64], String:sTemp[4][8]; + GetMenuItem(menu, param2, sOption, sizeof(sOption)); + ExplodeString(sOption, " ", sTemp, 4, 8); + // TargetID TYPE_BLOCK ReasonIndex LenghtIndex + new target = GetClientOfUserId(StringToInt(sTemp[0])); + + if (Bool_ValidMenuTarget(param1, target)) + { + new type = StringToInt(sTemp[1]); + new reasonIndex = StringToInt(sTemp[2]); + new lengthIndex = StringToInt(sTemp[3]); + new length; + if (lengthIndex >= 0 && lengthIndex <= iNumTimes) + length = g_iTimeMinutes[lengthIndex]; + else + { + length = DefaultTime; + LogError("Wrong length index in menu - using default time"); + } + + CreateBlock(param1, target, length, type, g_sReasonKey[reasonIndex]); + } + } + } +} + +AdminMenu_List(client, index) +{ + decl String:sTitle[192], String:sOption[32]; + Format(sTitle, sizeof(sTitle), "%T", "AdminMenu_Select_List", client); + new iClients, Handle:hMenu = CreateMenu(MenuHandler_MenuList); + SetMenuTitle(hMenu, sTitle); + if (!g_iPeskyPanels[client][viewingList]) + SetMenuExitBackButton(hMenu, true); + + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && !IsFakeClient(i) && (g_MuteType[i] > bNot || g_GagType[i] > bNot)) + { + iClients++; + strcopy(sTitle, sizeof(sTitle), g_sName[i]); + AdminMenu_GetPunishPhrase(client, i, sTitle, sizeof(sTitle)); + Format(sOption, sizeof(sOption), "%d", GetClientUserId(i)); + AddMenuItem(hMenu, sOption, sTitle); + } + } + + if (!iClients) + { + Format(sTitle, sizeof(sTitle), "%T", "ListMenu_Option_Empty", client); + AddMenuItem(hMenu, "0", sTitle, ITEMDRAW_DISABLED); + } + + DisplayMenuAtItem(hMenu, client, index, MENU_TIME_FOREVER); +} + +public MenuHandler_MenuList(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + CloseHandle(menu); + case MenuAction_Cancel: + { + if (!g_iPeskyPanels[param1][viewingList]) + if (param2 == MenuCancel_ExitBack && hTopMenu != INVALID_HANDLE) + DisplayTopMenu(hTopMenu, param1, TopMenuPosition_LastCategory); + } + case MenuAction_Select: + { + decl String:sOption[32]; + GetMenuItem(menu, param2, sOption, sizeof(sOption)); + new target = GetClientOfUserId(StringToInt(sOption)); + + if (Bool_ValidMenuTarget(param1, target)) + AdminMenu_ListTarget(param1, target, GetMenuSelectionPosition()); + else + AdminMenu_List(param1, GetMenuSelectionPosition()); + } + } +} + +AdminMenu_ListTarget(client, target, index, viewMute = 0, viewGag = 0) +{ + new userid = GetClientUserId(target), Handle:hMenu = CreateMenu(MenuHandler_MenuListTarget); + decl String:sBuffer[192], String:sOption[32]; + SetMenuTitle(hMenu, g_sName[target]); + SetMenuPagination(hMenu, MENU_NO_PAGINATION); + SetMenuExitButton(hMenu, true); + SetMenuExitBackButton(hMenu, false); + + if (g_MuteType[target] > bNot) + { + Format(sBuffer, sizeof(sBuffer), "%T", "ListMenu_Option_Mute", client); + Format(sOption, sizeof(sOption), "0 %d %d %b %b", userid, index, viewMute, viewGag); + AddMenuItem(hMenu, sOption, sBuffer); + + if (viewMute) + { + Format(sBuffer, sizeof(sBuffer), "%T", "ListMenu_Option_Admin", client, g_sMuteAdminName[target]); + AddMenuItem(hMenu, "", sBuffer, ITEMDRAW_DISABLED); + + decl String:sMuteTemp[192], String:_sMuteTime[192]; + Format(sMuteTemp, sizeof(sMuteTemp), "%T", "ListMenu_Option_Duration", client); + switch (g_MuteType[target]) + { + case bPerm:Format(sBuffer, sizeof(sBuffer), "%s%T", sMuteTemp, "ListMenu_Option_Duration_Perm", client); + case bTime:Format(sBuffer, sizeof(sBuffer), "%s%T", sMuteTemp, "ListMenu_Option_Duration_Time", client, g_iMuteLength[target]); + case bSess:Format(sBuffer, sizeof(sBuffer), "%s%T", sMuteTemp, "ListMenu_Option_Duration_Temp", client); + default:Format(sBuffer, sizeof(sBuffer), "error"); + } + AddMenuItem(hMenu, "", sBuffer, ITEMDRAW_DISABLED); + + FormatTime(_sMuteTime, sizeof(_sMuteTime), NULL_STRING, g_iMuteTime[target]); + Format(sBuffer, sizeof(sBuffer), "%T", "ListMenu_Option_Issue", client, _sMuteTime); + AddMenuItem(hMenu, "", sBuffer, ITEMDRAW_DISABLED); + + Format(sMuteTemp, sizeof(sMuteTemp), "%T", "ListMenu_Option_Expire", client); + switch (g_MuteType[target]) + { + case bTime: + { + FormatTime(_sMuteTime, sizeof(_sMuteTime), NULL_STRING, (g_iMuteTime[target] + g_iMuteLength[target] * 60)); + Format(sBuffer, sizeof(sBuffer), "%s%T", sMuteTemp, "ListMenu_Option_Expire_Time", client, _sMuteTime); + } + case bPerm:Format(sBuffer, sizeof(sBuffer), "%s%T", sMuteTemp, "ListMenu_Option_Expire_Perm", client); + case bSess:Format(sBuffer, sizeof(sBuffer), "%s%T", sMuteTemp, "ListMenu_Option_Expire_Temp_Reconnect", client); + default:Format(sBuffer, sizeof(sBuffer), "error"); + } + AddMenuItem(hMenu, "", sBuffer, ITEMDRAW_DISABLED); + + if (strlen(g_sMuteReason[target]) > 0) + { + Format(sBuffer, sizeof(sBuffer), "%T", "ListMenu_Option_Reason", client); + Format(sOption, sizeof(sOption), "1 %d %d %b %b", userid, index, viewMute, viewGag); + AddMenuItem(hMenu, sOption, sBuffer); + } + else + { + Format(sBuffer, sizeof(sBuffer), "%T", "ListMenu_Option_Reason_None", client); + AddMenuItem(hMenu, "", sBuffer, ITEMDRAW_DISABLED); + } + } + } + + if (g_GagType[target] > bNot) + { + Format(sBuffer, sizeof(sBuffer), "%T", "ListMenu_Option_Gag", client); + Format(sOption, sizeof(sOption), "2 %d %d %b %b", userid, index, viewMute, viewGag); + AddMenuItem(hMenu, sOption, sBuffer); + + if (viewGag) + { + Format(sBuffer, sizeof(sBuffer), "%T", "ListMenu_Option_Admin", client, g_sGagAdminName[target]); + AddMenuItem(hMenu, "", sBuffer, ITEMDRAW_DISABLED); + + decl String:sGagTemp[192], String:_sGagTime[192]; + Format(sGagTemp, sizeof(sGagTemp), "%T", "ListMenu_Option_Duration", client); + + switch (g_GagType[target]) + { + case bPerm:Format(sBuffer, sizeof(sBuffer), "%s%T", sGagTemp, "ListMenu_Option_Duration_Perm", client); + case bTime:Format(sBuffer, sizeof(sBuffer), "%s%T", sGagTemp, "ListMenu_Option_Duration_Time", client, g_iGagLength[target]); + case bSess:Format(sBuffer, sizeof(sBuffer), "%s%T", sGagTemp, "ListMenu_Option_Duration_Temp", client); + default:Format(sBuffer, sizeof(sBuffer), "error"); + } + + AddMenuItem(hMenu, "", sBuffer, ITEMDRAW_DISABLED); + + FormatTime(_sGagTime, sizeof(_sGagTime), NULL_STRING, g_iGagTime[target]); + Format(sBuffer, sizeof(sBuffer), "%T", "ListMenu_Option_Issue", client, _sGagTime); + AddMenuItem(hMenu, "", sBuffer, ITEMDRAW_DISABLED); + + Format(sGagTemp, sizeof(sGagTemp), "%T", "ListMenu_Option_Expire", client); + + switch (g_GagType[target]) + { + case bTime: + { + FormatTime(_sGagTime, sizeof(_sGagTime), NULL_STRING, (g_iGagTime[target] + g_iGagLength[target] * 60)); + Format(sBuffer, sizeof(sBuffer), "%s%T", sGagTemp, "ListMenu_Option_Expire_Time", client, _sGagTime); + } + case bPerm:Format(sBuffer, sizeof(sBuffer), "%s%T", sGagTemp, "ListMenu_Option_Expire_Perm", client); + case bSess:Format(sBuffer, sizeof(sBuffer), "%s%T", sGagTemp, "ListMenu_Option_Expire_Temp_Reconnect", client); + default:Format(sBuffer, sizeof(sBuffer), "error"); + } + + AddMenuItem(hMenu, "", sBuffer, ITEMDRAW_DISABLED); + + if (strlen(g_sGagReason[target]) > 0) + { + Format(sBuffer, sizeof(sBuffer), "%T", "ListMenu_Option_Reason", client); + Format(sOption, sizeof(sOption), "3 %d %d %b %b", userid, index, viewMute, viewGag); + AddMenuItem(hMenu, sOption, sBuffer); + } + else + { + Format(sBuffer, sizeof(sBuffer), "%T", "ListMenu_Option_Reason_None", client); + AddMenuItem(hMenu, "", sBuffer, ITEMDRAW_DISABLED); + } + } + } + + g_iPeskyPanels[client][curIndex] = index; + g_iPeskyPanels[client][curTarget] = target; + g_iPeskyPanels[client][viewingGag] = viewGag; + g_iPeskyPanels[client][viewingMute] = viewMute; + DisplayMenu(hMenu, client, MENU_TIME_FOREVER); +} + +public MenuHandler_MenuListTarget(Handle:menu, MenuAction:action, param1, param2) +{ + switch (action) + { + case MenuAction_End: + CloseHandle(menu); + case MenuAction_Cancel: + { + if (param2 == MenuCancel_ExitBack) + AdminMenu_List(param1, g_iPeskyPanels[param1][curIndex]); + } + case MenuAction_Select: + { + decl String:sOption[64], String:sTemp[5][8]; + GetMenuItem(menu, param2, sOption, sizeof(sOption)); + ExplodeString(sOption, " ", sTemp, 5, 8); + + new target = GetClientOfUserId(StringToInt(sTemp[1])); + if (param1 == target || Bool_ValidMenuTarget(param1, target)) + { + switch (StringToInt(sTemp[0])) + { + case 0: + AdminMenu_ListTarget(param1, target, StringToInt(sTemp[2]), !(StringToInt(sTemp[3])), 0); + case 1, 3: + AdminMenu_ListTargetReason(param1, target, g_iPeskyPanels[param1][viewingMute], g_iPeskyPanels[param1][viewingGag]); + case 2: + AdminMenu_ListTarget(param1, target, StringToInt(sTemp[2]), 0, !(StringToInt(sTemp[4]))); + } + } + else + AdminMenu_List(param1, StringToInt(sTemp[2])); + + } + } +} + +AdminMenu_ListTargetReason(client, target, showMute, showGag) +{ + decl String:sTemp[192], String:sBuffer[192]; + new Handle:hPanel = CreatePanel(); + SetPanelTitle(hPanel, g_sName[target]); + DrawPanelItem(hPanel, " ", ITEMDRAW_SPACER | ITEMDRAW_RAWLINE); + + if (showMute) + { + Format(sTemp, sizeof(sTemp), "%T", "ReasonPanel_Punishment_Mute", client); + switch (g_MuteType[target]) + { + case bPerm:Format(sBuffer, sizeof(sBuffer), "%s%T", sTemp, "ReasonPanel_Perm", client); + case bTime:Format(sBuffer, sizeof(sBuffer), "%s%T", sTemp, "ReasonPanel_Time", client, g_iMuteLength[target]); + case bSess:Format(sBuffer, sizeof(sBuffer), "%s%T", sTemp, "ReasonPanel_Temp", client); + default:Format(sBuffer, sizeof(sBuffer), "error"); + } + DrawPanelText(hPanel, sBuffer); + + Format(sBuffer, sizeof(sBuffer), "%T", "ReasonPanel_Reason", client, g_sMuteReason[target]); + DrawPanelText(hPanel, sBuffer); + } + else if (showGag) + { + Format(sTemp, sizeof(sTemp), "%T", "ReasonPanel_Punishment_Gag", client); + switch (g_GagType[target]) + { + case bPerm:Format(sBuffer, sizeof(sBuffer), "%s%T", sTemp, "ReasonPanel_Perm", client); + case bTime:Format(sBuffer, sizeof(sBuffer), "%s%T", sTemp, "ReasonPanel_Time", client, g_iGagLength[target]); + case bSess:Format(sBuffer, sizeof(sBuffer), "%s%T", sTemp, "ReasonPanel_Temp", client); + default:Format(sBuffer, sizeof(sBuffer), "error"); + } + DrawPanelText(hPanel, sBuffer); + + Format(sBuffer, sizeof(sBuffer), "%T", "ReasonPanel_Reason", client, g_sGagReason[target]); + DrawPanelText(hPanel, sBuffer); + } + + DrawPanelItem(hPanel, " ", ITEMDRAW_SPACER | ITEMDRAW_RAWLINE); + SetPanelCurrentKey(hPanel, 10); + Format(sBuffer, sizeof(sBuffer), "%T", "ReasonPanel_Back", client); + DrawPanelItem(hPanel, sBuffer); + SendPanelToClient(hPanel, client, PanelHandler_ListTargetReason, MENU_TIME_FOREVER); + CloseHandle(hPanel); +} + +public PanelHandler_ListTargetReason(Handle:menu, MenuAction:action, param1, param2) +{ + if (action == MenuAction_Select) + { + AdminMenu_ListTarget(param1, g_iPeskyPanels[param1][curTarget], + g_iPeskyPanels[param1][curIndex], + g_iPeskyPanels[param1][viewingMute], + g_iPeskyPanels[param1][viewingGag]); + } +} + + +// SQL CALLBACKS // + +public GotDatabase(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + #if defined DEBUG + PrintToServer("GotDatabase(data: %d, lock: %d, g_h: %d, hndl: %d)", data, g_iConnectLock, g_hDatabase, hndl); + #endif + + // If this happens to be an old connection request, ignore it. + if (data != g_iConnectLock || g_hDatabase) + { + if (hndl) + CloseHandle(hndl); + return; + } + + g_iConnectLock = 0; + g_DatabaseState = DatabaseState_Connected; + g_hDatabase = hndl; + + // See if the connection is valid. If not, don't un-mark the caches + // as needing rebuilding, in case the next connection request works. + if (!g_hDatabase) + { + LogError("Connecting to database failed: %s", error); + return; + } + + // Set character set to UTF-8 in the database + if (GetFeatureStatus(FeatureType_Native, "SQL_SetCharset") == FeatureStatus_Available) + { + SQL_SetCharset(g_hDatabase, "utf8"); + } + else + { + decl String:query[128]; + FormatEx(query, sizeof(query), "SET NAMES 'UTF8'"); + #if defined LOG_QUERIES + LogToFile(logQuery, "Set encoding. QUERY: %s", query); + #endif + SQL_TQuery(g_hDatabase, Query_ErrorCheck, query); + } + + // Process queue + SQL_TQuery(SQLiteDB, Query_ProcessQueue, + "SELECT id, steam_id, time, start_time, reason, name, admin_id, admin_ip, type \ + FROM queue2"); + + // Force recheck players + ForcePlayersRecheck(); +} + +public Query_AddBlockInsert(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + ResetPack(data); + + decl String:reason[256]; + + new iAdminUserId = ReadPackCell(data); + new iAdmin = 0; + + if(iAdminUserId > 0) { + iAdmin = GetClientOfUserId(iAdminUserId); + } + + new iTarget = GetClientOfUserId(ReadPackCell(data)); + + if (!iTarget) { + iTarget = -1; + } + + new length = ReadPackCell(data); + new type = ReadPackCell(data); + ReadPackString(data, reason, sizeof(reason)); + + // Fire forward + Call_StartForward(g_hFwd_OnPlayerPunished); + Call_PushCell(iAdmin); + Call_PushCell(iTarget); + Call_PushCell(length); + Call_PushCell(type); + Call_PushString(reason); + Call_Finish(); + + if (DB_Conn_Lost(hndl) || error[0]) + { + LogError("Query_AddBlockInsert failed: %s", error); + + decl String:name[MAX_NAME_LENGTH], String:auth[64], String:adminAuth[32], String:adminIp[20]; + ReadPackString(data, name, sizeof(name)); + ReadPackString(data, auth, sizeof(auth)); + ReadPackString(data, adminAuth, sizeof(adminAuth)); + ReadPackString(data, adminIp, sizeof(adminIp)); + + InsertTempBlock(length, type, name, auth, reason, adminAuth, adminIp); + } + CloseHandle(data); +} + +public Query_UnBlockSelect(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + decl String:adminAuth[30], String:targetAuth[30]; + new String:reason[256]; + + ResetPack(data); + new adminUserID = ReadPackCell(data); + new targetUserID = ReadPackCell(data); + new type = ReadPackCell(data); // not in use unless DEBUG + ReadPackString(data, adminAuth, sizeof(adminAuth)); + ReadPackString(data, targetAuth, sizeof(targetAuth)); + ReadPackString(data, reason, sizeof(reason)); + + new admin = GetClientOfUserId(adminUserID); + new target = GetClientOfUserId(targetUserID); + + #if defined DEBUG + PrintToServer("Query_UnBlockSelect(adminUID: %d/%d, targetUID: %d/%d, type: %d, adminAuth: %s, targetAuth: %s, reason: %s)", + adminUserID, admin, targetUserID, target, type, adminAuth, targetAuth, reason); + #endif + + decl String:targetName[MAX_NAME_LENGTH]; + strcopy(targetName, MAX_NAME_LENGTH, target && IsClientInGame(target) ? g_sName[target] : targetAuth); //FIXME + + new bool:hasErrors = false; + // If error is not an empty string the query failed + if (DB_Conn_Lost(hndl) || error[0] != '\0') + { + LogError("Query_UnBlockSelect failed: %s", error); + if (admin && IsClientInGame(admin)) + { + PrintToChat(admin, "%s%T", PREFIX, "Unblock Select Failed", admin, targetAuth); + PrintToConsole(admin, "%s%T", PREFIX, "Unblock Select Failed", admin, targetAuth); + } + else + { + PrintToServer("%s%T", PREFIX, "Unblock Select Failed", LANG_SERVER, targetAuth); + } + hasErrors = true; + } + + // If there was no results then a ban does not exist for that id + if (!DB_Conn_Lost(hndl) && !SQL_GetRowCount(hndl)) + { + if (admin && IsClientInGame(admin)) + { + PrintToChat(admin, "%s%t", PREFIX, "No blocks found", targetAuth); + PrintToConsole(admin, "%s%t", PREFIX, "No blocks found", targetAuth); + } + else + { + PrintToServer("%s%T", PREFIX, "No blocks found", LANG_SERVER, targetAuth); + } + hasErrors = true; + } + + if (hasErrors) + { + #if defined DEBUG + PrintToServer("Calling TempUnBlock from Query_UnBlockSelect"); + #endif + + TempUnBlock(data); // Datapack closed inside. + return; + } + else + { + new bool:b_success = false; + // Get the values from the founded blocks. + while (SQL_MoreRows(hndl)) + { + // Oh noes! What happened?! + if (!SQL_FetchRow(hndl)) + continue; + + new bid = SQL_FetchInt(hndl, 0); + new iAID = SQL_FetchInt(hndl, 1); + new cAID = SQL_FetchInt(hndl, 2); + new cImmunity = SQL_FetchInt(hndl, 3); + new cType = SQL_FetchInt(hndl, 4); + + #if defined DEBUG + PrintToServer("Fetched from DB: bid %d, iAID: %d, cAID: %d, cImmunity: %d, cType: %d", bid, iAID, cAID, cImmunity, cType); + // WHO WE ARE? + PrintToServer("WHO WE ARE CHECKING!"); + if (iAID == cAID) + PrintToServer("we are block author"); + if (!admin) + PrintToServer("we are console (possibly)"); + if (AdmHasFlag(admin)) + PrintToServer("we have special flag"); + if (GetAdmImmunity(admin) > cImmunity) + PrintToServer("we have %d immunity and block has %d. we cool", GetAdmImmunity(admin), cImmunity); + #endif + + // Checking - has we access to unblock? + if (iAID == cAID || (!admin && StrEqual(adminAuth, "STEAM_ID_SERVER")) || AdmHasFlag(admin) || (DisUBImCheck == 0 && (GetAdmImmunity(admin) > cImmunity))) + { + // Ok! we have rights to unblock + b_success = true; + // UnMute/UnGag, Show & log activity + if (target && IsClientInGame(target)) + { + switch (cType) + { + case TYPE_MUTE: + { + PerformUnMute(target); + LogAction(admin, target, "\"%L\" unmuted \"%L\" (reason \"%s\")", admin, target, reason); + } + //------------------------------------------------------------------------------------------------- + case TYPE_GAG: + { + PerformUnGag(target); + LogAction(admin, target, "\"%L\" ungagged \"%L\" (reason \"%s\")", admin, target, reason); + } + } + } + + new Handle:dataPack = CreateDataPack(); + WritePackCell(dataPack, adminUserID); + WritePackCell(dataPack, cType); + WritePackString(dataPack, g_sName[target]); + WritePackString(dataPack, targetAuth); + + decl String:unbanReason[sizeof(reason) * 2 + 1]; + SQL_EscapeString(g_hDatabase, reason, unbanReason, sizeof(unbanReason)); + + decl String:query[2048]; + Format(query, sizeof(query), + "UPDATE %s_comms \ + SET RemovedBy = %d, \ + RemoveType = 'U', \ + RemovedOn = UNIX_TIMESTAMP(), \ + ureason = '%s' \ + WHERE bid = %d", + DatabasePrefix, iAID, unbanReason, bid); + #if defined LOG_QUERIES + LogToFile(logQuery, "Query_UnBlockSelect. QUERY: %s", query); + #endif + SQL_TQuery(g_hDatabase, Query_UnBlockUpdate, query, dataPack); + } + else + { + // sorry, we don't have permission to unblock! + #if defined DEBUG + PrintToServer("No permissions to unblock in Query_UnBlockSelect"); + #endif + switch (cType) + { + case TYPE_MUTE: + { + if (admin && IsClientInGame(admin)) + { + PrintToChat(admin, "%s%t", PREFIX, "No permission unmute", targetName); + PrintToConsole(admin, "%s%t", PREFIX, "No permission unmute", targetName); + } + LogAction(admin, target, "\"%L\" tried (and didn't have permission) to unmute %s (reason \"%s\")", admin, targetAuth, reason); + } + //------------------------------------------------------------------------------------------------- + case TYPE_GAG: + { + if (admin && IsClientInGame(admin)) + { + PrintToChat(admin, "%s%t", PREFIX, "No permission ungag", targetName); + PrintToConsole(admin, "%s%t", PREFIX, "No permission ungag", targetName); + } + LogAction(admin, target, "\"%L\" tried (and didn't have permission) to ungag %s (reason \"%s\")", admin, targetAuth, reason); + } + } + } + } + + if (b_success && target && IsClientInGame(target)) + { + #if defined DEBUG + PrintToServer("Showing activity to server in Query_UnBlockSelect"); + #endif + ShowActivityToServer(admin, type, _, _, g_sName[target], _); + + if (type == TYPE_UNSILENCE) + { + // check result for possible combination with temp and time punishments (temp was skipped in code above) + + #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 8 + SetPackPosition(data, view_as(16)); + #else + SetPackPosition(data, 16); + #endif + + if (g_MuteType[target] > bNot) + { + WritePackCell(data, TYPE_UNMUTE); + TempUnBlock(data); + data = INVALID_HANDLE; + } + else if (g_GagType[target] > bNot) + { + WritePackCell(data, TYPE_UNGAG); + TempUnBlock(data); + data = INVALID_HANDLE; + } + } + } + } + if (data != INVALID_HANDLE) + CloseHandle(data); +} + +public Query_UnBlockUpdate(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + new admin, type; + decl String:targetName[MAX_NAME_LENGTH], String:targetAuth[30]; + + ResetPack(data); + admin = GetClientOfUserId(ReadPackCell(data)); + type = ReadPackCell(data); + ReadPackString(data, targetName, sizeof(targetName)); + ReadPackString(data, targetAuth, sizeof(targetAuth)); + CloseHandle(data); + + if (DB_Conn_Lost(hndl) || error[0] != '\0') + { + LogError("Query_UnBlockUpdate failed: %s", error); + if (admin && IsClientInGame(admin)) + { + PrintToChat(admin, "%s%t", PREFIX, "Unblock insert failed"); + PrintToConsole(admin, "%s%t", PREFIX, "Unblock insert failed"); + } + return; + } + + switch (type) + { + case TYPE_MUTE: + { + LogAction(admin, -1, "\"%L\" removed mute for %s from DB", admin, targetAuth); + if (admin && IsClientInGame(admin)) + { + PrintToChat(admin, "%s%t", PREFIX, "successfully unmuted", targetName); + PrintToConsole(admin, "%s%t", PREFIX, "successfully unmuted", targetName); + } + else + { + PrintToServer("%s%T", PREFIX, "successfully unmuted", LANG_SERVER, targetName); + } + } + //------------------------------------------------------------------------------------------------- + case TYPE_GAG: + { + LogAction(admin, -1, "\"%L\" removed gag for %s from DB", admin, targetAuth); + if (admin && IsClientInGame(admin)) { + PrintToChat(admin, "%s%t", PREFIX, "successfully ungagged", targetName); + PrintToConsole(admin, "%s%t", PREFIX, "successfully ungagged", targetName); + } + else + { + PrintToServer("%s%T", PREFIX, "successfully ungagged", LANG_SERVER, targetName); + } + } + } +} + +// ProcessQueueCallback is called as the result of selecting all the rows from the queue table +public Query_ProcessQueue(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + if (hndl == INVALID_HANDLE || error[0]) + { + LogError("Query_ProcessQueue failed: %s", error); + return; + } + + decl String:auth[64]; + decl String:name[MAX_NAME_LENGTH]; + new String:reason[256]; + decl String:adminAuth[64], String:adminIp[20]; + decl String:query[4096]; + + while (SQL_MoreRows(hndl)) + { + // Oh noes! What happened?! + if (!SQL_FetchRow(hndl)) + continue; + + decl String:sAuthEscaped[sizeof(auth) * 2 + 1]; + decl String:banName[MAX_NAME_LENGTH * 2 + 1]; + decl String:banReason[sizeof(reason) * 2 + 1]; + decl String:sAdmAuthEscaped[sizeof(adminAuth) * 2 + 1]; + decl String:sAdmAuthYZEscaped[sizeof(adminAuth) * 2 + 1]; + + // if we get to here then there are rows in the queue pending processing + //steam_id TEXT, time INTEGER, start_time INTEGER, reason TEXT, name TEXT, admin_id TEXT, admin_ip TEXT, type INTEGER + new id = SQL_FetchInt(hndl, 0); + SQL_FetchString(hndl, 1, auth, sizeof(auth)); + new time = SQL_FetchInt(hndl, 2); + new startTime = SQL_FetchInt(hndl, 3); + SQL_FetchString(hndl, 4, reason, sizeof(reason)); + SQL_FetchString(hndl, 5, name, sizeof(name)); + SQL_FetchString(hndl, 6, adminAuth, sizeof(adminAuth)); + SQL_FetchString(hndl, 7, adminIp, sizeof(adminIp)); + new type = SQL_FetchInt(hndl, 8); + + if (DB_Connect()) { + SQL_EscapeString(g_hDatabase, auth, sAuthEscaped, sizeof(sAuthEscaped)); + SQL_EscapeString(g_hDatabase, name, banName, sizeof(banName)); + SQL_EscapeString(g_hDatabase, reason, banReason, sizeof(banReason)); + SQL_EscapeString(g_hDatabase, adminAuth, sAdmAuthEscaped, sizeof(sAdmAuthEscaped)); + SQL_EscapeString(g_hDatabase, adminAuth[8], sAdmAuthYZEscaped, sizeof(sAdmAuthYZEscaped)); + } + else + continue; + // all blocks should be entered into db! + + FormatEx(query, sizeof(query), + "INSERT INTO %s_comms (authid, name, created, ends, length, reason, aid, adminIp, sid, type) \ + VALUES ('%s', '%s', %d, %d, %d, '%s', \ + IFNULL((SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '0'), \ + '%s', %d, %d)", + DatabasePrefix, sAuthEscaped, banName, startTime, (startTime + (time * 60)), (time * 60), banReason, DatabasePrefix, sAdmAuthEscaped, sAdmAuthYZEscaped, adminIp, serverID, type); + #if defined LOG_QUERIES + LogToFile(logQuery, "Query_ProcessQueue. QUERY: %s", query); + #endif + SQL_TQuery(g_hDatabase, Query_AddBlockFromQueue, query, id); + } +} + +public Query_AddBlockFromQueue(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + decl String:query[512]; + if (error[0] == '\0') + { + // The insert was successful so delete the record from the queue + FormatEx(query, sizeof(query), + "DELETE FROM queue2 \ + WHERE id = %d", + data); + #if defined LOG_QUERIES + LogToFile(logQuery, "Query_AddBlockFromQueue. QUERY: %s", query); + #endif + SQL_TQuery(SQLiteDB, Query_ErrorCheck, query); + } +} + +public Query_ErrorCheck(Handle:owner, Handle:hndl, const String:error[], any:data) +{ + if (DB_Conn_Lost(hndl) || error[0]) + LogError("%T (%s)", "Failed to query database", LANG_SERVER, error); +} + +public Query_VerifyBlock(Handle:owner, Handle:hndl, const String:error[], any:userid) +{ + decl String:clientAuth[64]; + new client = GetClientOfUserId(userid); + + #if defined DEBUG + PrintToServer("Query_VerifyBlock(userid: %d, client: %d)", userid, client); + #endif + + if (!client) + return; + + /* Failure happen. Do retry with delay */ + if (DB_Conn_Lost(hndl)) + { + LogError("Query_VerifyBlock failed: %s", error); + if (g_hPlayerRecheck[client] == INVALID_HANDLE) + g_hPlayerRecheck[client] = CreateTimer(RetryTime, ClientRecheck, userid); + return; + } + + GetClientAuthId(client, AuthId_Steam2, clientAuth, sizeof(clientAuth)); + + //SELECT (c.ends - UNIX_TIMESTAMP()) as remaining, c.length, c.type, c.created, c.reason, a.user, + //IF (a.immunity>=g.immunity, a.immunity, IFNULL(g.immunity,0)) as immunity, c.aid, c.sid, c.authid + //FROM %s_comms c LEFT JOIN %s_admins a ON a.aid=c.aid LEFT JOIN %s_srvgroups g ON g.name = a.srv_group + //WHERE c.authid REGEXP '^STEAM_[0-9]:%s$' AND (length = '0' OR ends > UNIX_TIMESTAMP()) AND RemoveType IS NULL", + if (SQL_GetRowCount(hndl) > 0) + { + while (SQL_FetchRow(hndl)) + { + if (NotApplyToThisServer(SQL_FetchInt(hndl, 8))) + continue; + + decl String:sAdmName[MAX_NAME_LENGTH], String:sAdmAuth[64]; + new String:sReason[256]; + new remaining_time = SQL_FetchInt(hndl, 0); + new length = SQL_FetchInt(hndl, 1); + new type = SQL_FetchInt(hndl, 2); + new time = SQL_FetchInt(hndl, 3); + SQL_FetchString(hndl, 4, sReason, sizeof(sReason)); + SQL_FetchString(hndl, 5, sAdmName, sizeof(sAdmName)); + new immunity = SQL_FetchInt(hndl, 6); + new aid = SQL_FetchInt(hndl, 7); + SQL_FetchString(hndl, 9, sAdmAuth, sizeof(sAdmAuth)); + + // Block from CONSOLE (aid=0) and we have `console immunity` value in config + if (!aid && ConsoleImmunity > immunity) + immunity = ConsoleImmunity; + + #if defined DEBUG + PrintToServer("Fetched from DB: remaining %d, length %d, type %d", remaining_time, length, type); + #endif + + switch (type) + { + case TYPE_MUTE: + { + //Set mute type based on length + if (length > 0) + g_MuteType[client] = bTime; + else if (length == 0) + g_MuteType[client] = bPerm; + else + g_MuteType[client] = bSess; + + //Perform mute/unmute + if (g_MuteType[client] == bSess) + { + PerformUnMute(client); + } + else if (g_MuteType[client] > bSess) + { + PerformMute(client, time, length / 60, sAdmName, sAdmAuth, immunity, sReason, remaining_time); + PrintToChat(client, "%s%t", PREFIX, "Muted on connect"); + } + } + case TYPE_GAG: + { + //Set gag type based on length + if (length > 0) + g_GagType[client] = bTime; + else if (length == 0) + g_GagType[client] = bPerm; + else + g_GagType[client] = bSess; + + //Perform gag/ungag + if (g_GagType[client] == bSess) + { + PerformUnGag(client); + } + else if (g_GagType[client] > bSess) + { + PerformGag(client, time, length / 60, sAdmName, sAdmAuth, immunity, sReason, remaining_time); + PrintToChat(client, "%s%t", PREFIX, "Gagged on connect"); + } + } + } + } + } + + g_bPlayerStatus[client] = true; +} + + +// TIMER CALL BACKS // + +public Action:ClientRecheck(Handle:timer, any:userid) +{ + #if defined DEBUG + PrintToServer("ClientRecheck(userid: %d)", userid); + #endif + + new client = GetClientOfUserId(userid); + if (!client) + return; + + if (IsClientConnected(client)) + OnClientPostAdminCheck(client); + + g_hPlayerRecheck[client] = INVALID_HANDLE; +} + +public Action:Timer_MuteExpire(Handle:timer, Handle:hPack) +{ + ResetPack(hPack); + g_hMuteExpireTimer[ReadPackCell(hPack)] = INVALID_HANDLE; + + new client = GetClientOfUserId(ReadPackCell(hPack)); + if (!client) + return; + + #if defined DEBUG + decl String:clientAuth[64]; + GetClientAuthId(client, AuthId_Steam2, clientAuth, sizeof(clientAuth)); + PrintToServer("Mute expired for %s", clientAuth); + #endif + + PrintToChat(client, "%s%t", PREFIX, "Mute expired"); + + MarkClientAsUnMuted(client); + if (IsClientInGame(client)) + BaseComm_SetClientMute(client, false); +} + +public Action:Timer_GagExpire(Handle:timer, Handle:hPack) +{ + ResetPack(hPack); + g_hGagExpireTimer[ReadPackCell(hPack)] = INVALID_HANDLE; + + new client = GetClientOfUserId(ReadPackCell(hPack)); + if (!client) + return; + + #if defined DEBUG + decl String:clientAuth[64]; + GetClientAuthId(client, AuthId_Steam2, clientAuth, sizeof(clientAuth)); + PrintToServer("Gag expired for %s", clientAuth); + #endif + + PrintToChat(client, "%s%t", PREFIX, "Gag expired"); + + MarkClientAsUnGagged(client); + if (IsClientInGame(client)) + BaseComm_SetClientGag(client, false); +} + +public Action:Timer_StopWait(Handle:timer, any:data) +{ + g_DatabaseState = DatabaseState_None; + DB_Connect(); +} + +// PARSER // + +static InitializeConfigParser() +{ + if (ConfigParser == INVALID_HANDLE) + { + ConfigParser = SMC_CreateParser(); + SMC_SetReaders(ConfigParser, ReadConfig_NewSection, ReadConfig_KeyValue, ReadConfig_EndSection); + } +} + +static InternalReadConfig(const String:path[]) +{ + ConfigState = ConfigStateNone; + + new SMCError:err = SMC_ParseFile(ConfigParser, path); + + if (err != SMCError_Okay) + { + decl String:buffer[64]; + PrintToServer("%s", SMC_GetErrorString(err, buffer, sizeof(buffer)) ? buffer : "Fatal parse error"); + } +} + +public SMCResult:ReadConfig_NewSection(Handle:smc, const String:name[], bool:opt_quotes) +{ + if (name[0]) + { + if (strcmp("Config", name, false) == 0) + { + ConfigState = ConfigStateConfig; + } + else if (strcmp("CommsReasons", name, false) == 0) + { + ConfigState = ConfigStateReasons; + } + else if (strcmp("CommsTimes", name, false) == 0) + { + ConfigState = ConfigStateTimes; + } + else if (strcmp("ServersWhiteList", name, false) == 0) + { + ConfigState = ConfigStateServers; + } + } + return SMCParse_Continue; +} + +public SMCResult:ReadConfig_KeyValue(Handle:smc, const String:key[], const String:value[], bool:key_quotes, bool:value_quotes) +{ + if (!key[0]) + return SMCParse_Continue; + + switch (ConfigState) + { + case ConfigStateConfig: + { + if (strcmp("DatabasePrefix", key, false) == 0) + { + strcopy(DatabasePrefix, sizeof(DatabasePrefix), value); + + if (DatabasePrefix[0] == '\0') + { + DatabasePrefix = "sb"; + } + } + else if (strcmp("RetryTime", key, false) == 0) + { + RetryTime = StringToFloat(value); + if (RetryTime < 15.0) + { + RetryTime = 15.0; + } + else if (RetryTime > 60.0) + { + RetryTime = 60.0; + } + } + else if (strcmp("ServerID", key, false) == 0) + { + if (!StringToIntEx(value, serverID) || serverID < 1) + { + serverID = 0; + } + } + else if (strcmp("DefaultTime", key, false) == 0) + { + DefaultTime = StringToInt(value); + if (DefaultTime < 0) + { + DefaultTime = -1; + } + if (DefaultTime == 0) + { + DefaultTime = 30; + } + } + else if (strcmp("DisableUnblockImmunityCheck", key, false) == 0) + { + DisUBImCheck = StringToInt(value); + if (DisUBImCheck != 1) + { + DisUBImCheck = 0; + } + } + else if (strcmp("ConsoleImmunity", key, false) == 0) + { + ConsoleImmunity = StringToInt(value); + if (ConsoleImmunity < 0 || ConsoleImmunity > 100) + { + ConsoleImmunity = 0; + } + } + else if (strcmp("MaxLength", key, false) == 0) + { + ConfigMaxLength = StringToInt(value); + } + else if (strcmp("OnlyWhiteListServers", key, false) == 0) + { + ConfigWhiteListOnly = StringToInt(value); + if (ConfigWhiteListOnly != 1) + { + ConfigWhiteListOnly = 0; + } + } + } + case ConfigStateReasons: + { + Format(g_sReasonKey[iNumReasons], REASON_SIZE, "%s", key); + Format(g_sReasonDisplays[iNumReasons], DISPLAY_SIZE, "%s", value); + #if defined DEBUG + PrintToServer("Loaded reason. index %d, key \"%s\", display_text \"%s\"", iNumReasons, g_sReasonKey[iNumReasons], g_sReasonDisplays[iNumReasons]); + #endif + iNumReasons++; + } + case ConfigStateTimes: + { + Format(g_sTimeDisplays[iNumTimes], DISPLAY_SIZE, "%s", value); + g_iTimeMinutes[iNumTimes] = StringToInt(key); + #if defined DEBUG + PrintToServer("Loaded time. index %d, time %d minutes, display_text \"%s\"", iNumTimes, g_iTimeMinutes[iNumTimes], g_sTimeDisplays[iNumTimes]); + #endif + iNumTimes++; + } + case ConfigStateServers: + { + if (strcmp("id", key, false) == 0) + { + new srvID = StringToInt(value); + if (srvID >= 0) + { + PushArrayCell(g_hServersWhiteList, srvID); + #if defined DEBUG + PrintToServer("Loaded white list server id %d", srvID); + #endif + } + } + } + } + return SMCParse_Continue; +} + +public SMCResult:ReadConfig_EndSection(Handle:smc) +{ + return SMCParse_Continue; +} + +// STOCK FUNCTIONS // +stock setGag(client, length, const String:clientAuth[]) +{ + if (g_GagType[client] == bNot) + { + PerformGag(client, _, length / 60, _, _, _, _); + PrintToChat(client, "%s%t", PREFIX, "Gagged on connect"); + LogMessage("%s is gagged from web", clientAuth); + } +} + +stock setMute(client, length, const String:clientAuth[]) +{ + if (g_MuteType[client] == bNot) + { + PerformMute(client, _, length / 60, _, _, _, _); + PrintToChat(client, "%s%t", PREFIX, "Muted on connect"); + LogMessage("%s is muted from web", clientAuth); + } +} + +stock bool:DB_Connect() +{ + #if defined DEBUG + PrintToServer("DB_Connect(handle %d, state %d, lock %d)", g_hDatabase, g_DatabaseState, g_iConnectLock); + #endif + + if (g_hDatabase) + { + return true; + } + + if (g_DatabaseState == DatabaseState_Wait) // 100500 connections in a minute is bad idea.. + { + return false; + } + + if (g_DatabaseState != DatabaseState_Connecting) + { + g_DatabaseState = DatabaseState_Connecting; + g_iConnectLock = ++g_iSequence; + // Connect using the "sourcebans" section, or the "default" section if "sourcebans" does not exist + SQL_TConnect(GotDatabase, DATABASE, g_iConnectLock); + } + + return false; +} + +stock bool:DB_Conn_Lost(Handle:hndl) +{ + if (hndl == INVALID_HANDLE) + { + if (g_hDatabase != INVALID_HANDLE) + { + LogError("Lost connection to DB. Reconnect after delay."); + CloseHandle(g_hDatabase); + g_hDatabase = INVALID_HANDLE; + } + if (g_DatabaseState != DatabaseState_Wait) + { + g_DatabaseState = DatabaseState_Wait; + CreateTimer(RetryTime, Timer_StopWait, _, TIMER_FLAG_NO_MAPCHANGE); + } + return true; + } + + return false; +} + +stock InitializeBackupDB() +{ + decl String:error[255]; + SQLiteDB = SQLite_UseDatabase("sourcecomms-queue", error, sizeof(error)); + if (SQLiteDB == INVALID_HANDLE) + { + SetFailState(error); + } + + SQL_TQuery(SQLiteDB, Query_ErrorCheck, + "CREATE TABLE IF NOT EXISTS queue2 ( \ + id INTEGER PRIMARY KEY, \ + steam_id TEXT, \ + time INTEGER, \ + start_time INTEGER, \ + reason TEXT, \ + name TEXT, \ + admin_id TEXT, \ + admin_ip TEXT, \ + type INTEGER)"); +} + +stock CreateBlock(client, targetId = 0, length = -1, type, const String:sReason[] = "", const String:sArgs[] = "") +{ + #if defined DEBUG + PrintToServer("CreateBlock(admin: %d, target: %d, length: %d, type: %d, reason: %s, args: %s)", client, targetId, length, type, sReason, sArgs); + #endif + + decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml, String:target_name[MAX_NAME_LENGTH]; + new String:reason[256]; + new bool:skipped = false; + + // checking args + if (targetId) + { + target_list[0] = targetId; + target_count = 1; + tn_is_ml = false; + strcopy(target_name, sizeof(target_name), g_sName[targetId]); + strcopy(reason, sizeof(reason), sReason); + } + else if (strlen(sArgs)) + { + new String:sArg[3][192]; + + if (ExplodeString(sArgs, "\"", sArg, 3, 192, true) == 3 && strlen(sArg[0]) == 0) // exploding by quotes + { + decl String:sTempArg[2][192]; + TrimString(sArg[2]); + sArg[0] = sArg[1]; // target name + ExplodeString(sArg[2], " ", sTempArg, 2, 192, true); // get length and reason + sArg[1] = sTempArg[0]; // lenght + sArg[2] = sTempArg[1]; // reason + } + else + { + ExplodeString(sArgs, " ", sArg, 3, 192, true); // exploding by spaces + } + + // Get the target, find target returns a message on failure so we do not + if ((target_count = ProcessTargetString( + sArg[0], + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_NO_BOTS, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + ReplyToTargetError(client, target_count); + return; + } + + // Get the block length + if (!StringToIntEx(sArg[1], length)) // not valid number in second argument + { + length = DefaultTime; + Format(reason, sizeof(reason), "%s %s", sArg[1], sArg[2]); + } + else + { + strcopy(reason, sizeof(reason), sArg[2]); + } + + // Strip spaces and quotes from reason + TrimString(reason); + StripQuotes(reason); + + if (!IsAllowedBlockLength(client, length, target_count)) + { + ReplyToCommand(client, "%s%t", PREFIX, "no access"); + return; + } + } + else + { + return; + } + + new admImmunity = GetAdmImmunity(client); + decl String:adminAuth[64]; + + if (client && IsClientInGame(client)) + { + GetClientAuthId(client, AuthId_Steam2, adminAuth, sizeof(adminAuth)); + } + else + { + // setup dummy adminAuth and adminIp for server + strcopy(adminAuth, sizeof(adminAuth), "STEAM_ID_SERVER"); + } + + for (new i = 0; i < target_count; i++) + { + new target = target_list[i]; + + #if defined DEBUG + decl String:auth[64]; + GetClientAuthId(target, AuthId_Steam2, auth, sizeof(auth)); + PrintToServer("Processing block for %s", auth); + #endif + + if (!g_bPlayerStatus[target]) + { + // The target has not been blocks verify. It must be completed before you can block anyone. + ReplyToCommand(client, "%s%t", PREFIX, "Player Comms Not Verified"); + skipped = true; + continue; // skip + } + + switch (type) + { + case TYPE_MUTE: + { + if (!BaseComm_IsClientMuted(target)) + { + #if defined DEBUG + PrintToServer("%s not muted. Mute him, creating unmute timer and add record to DB", auth); + #endif + + PerformMute(target, _, length, g_sName[client], adminAuth, admImmunity, reason); + + LogAction(client, target, "\"%L\" muted \"%L\" (minutes \"%d\") (reason \"%s\")", client, target, length, reason); + } + else + { + #if defined DEBUG + PrintToServer("%s already muted", auth); + #endif + + ReplyToCommand(client, "%s%t", PREFIX, "Player already muted", g_sName[target]); + + skipped = true; + continue; + } + } + //------------------------------------------------------------------------------------------------- + case TYPE_GAG: + { + if (!BaseComm_IsClientGagged(target)) + { + #if defined DEBUG + PrintToServer("%s not gagged. Gag him, creating ungag timer and add record to DB", auth); + #endif + + PerformGag(target, _, length, g_sName[client], adminAuth, admImmunity, reason); + + LogAction(client, target, "\"%L\" gagged \"%L\" (minutes \"%d\") (reason \"%s\")", client, target, length, reason); + } + else + { + #if defined DEBUG + PrintToServer("%s already gagged", auth); + #endif + + ReplyToCommand(client, "%s%t", PREFIX, "Player already gagged", g_sName[target]); + + skipped = true; + continue; + } + } + //------------------------------------------------------------------------------------------------- + case TYPE_SILENCE: + { + if (!BaseComm_IsClientGagged(target) && !BaseComm_IsClientMuted(target)) + { + #if defined DEBUG + PrintToServer("%s not silenced. Silence him, creating ungag & unmute timers and add records to DB", auth); + #endif + + PerformMute(target, _, length, g_sName[client], adminAuth, admImmunity, reason); + PerformGag(target, _, length, g_sName[client], adminAuth, admImmunity, reason); + + LogAction(client, target, "\"%L\" silenced \"%L\" (minutes \"%d\") (reason \"%s\")", client, target, length, reason); + } + else + { + #if defined DEBUG + PrintToServer("%s already gagged or/and muted", auth); + #endif + + ReplyToCommand(client, "%s%t", PREFIX, "Player already silenced", g_sName[target]); + + skipped = true; + continue; + } + } + } + } + if (target_count == 1 && !skipped) + SavePunishment(client, target_list[0], type, length, reason); + if (target_count > 1 || !skipped) + ShowActivityToServer(client, type, length, reason, target_name, tn_is_ml); + + return; +} + +stock ProcessUnBlock(client, targetId = 0, type, String:sReason[] = "", const String:sArgs[] = "") +{ + #if defined DEBUG + PrintToServer("ProcessUnBlock(admin: %d, target: %d, type: %d, reason: %s, args: %s)", client, targetId, type, sReason, sArgs); + #endif + + decl target_list[MAXPLAYERS], target_count, bool:tn_is_ml, String:target_name[MAX_NAME_LENGTH]; + new String:reason[256]; + + if (targetId) + { + target_list[0] = targetId; + target_count = 1; + tn_is_ml = false; + strcopy(target_name, sizeof(target_name), g_sName[targetId]); + strcopy(reason, sizeof(reason), sReason); + } + else + { + decl String:sBuffer[256]; + new String:sArg[3][192]; + GetCmdArgString(sBuffer, sizeof(sBuffer)); + + if (ExplodeString(sBuffer, "\"", sArg, 3, 192, true) == 3 && strlen(sArg[0]) == 0) + { + TrimString(sArg[2]); + sArg[0] = sArg[1]; // target name + sArg[1] = sArg[2]; // reason; sArg[2] - not in use + } + else + { + ExplodeString(sBuffer, " ", sArg, 2, 192, true); + } + strcopy(reason, sizeof(reason), sArg[1]); + // Strip spaces and quotes from reason + TrimString(reason); + StripQuotes(reason); + + // Get the target, find target returns a message on failure so we do not + if ((target_count = ProcessTargetString( + sArg[0], + client, + target_list, + MAXPLAYERS, + COMMAND_FILTER_NO_BOTS, + target_name, + sizeof(target_name), + tn_is_ml)) <= 0) + { + ReplyToTargetError(client, target_count); + return; + } + } + + decl String:adminAuth[64]; + decl String:targetAuth[64]; + + if (client && IsClientInGame(client)) + { + GetClientAuthId(client, AuthId_Steam2, adminAuth, sizeof(adminAuth)); + } + else + { + // setup dummy adminAuth and adminIp for server + strcopy(adminAuth, sizeof(adminAuth), "STEAM_ID_SERVER"); + } + + if (target_count > 1) + { + #if defined DEBUG + PrintToServer("ProcessUnBlock - targets_count > 1"); + #endif + + for (new i = 0; i < target_count; i++) + { + new target = target_list[i]; + + if (IsClientInGame(target)) + GetClientAuthId(target, AuthId_Steam2, targetAuth, sizeof(targetAuth)); + else + continue; + + switch (type) + { + case TYPE_UNMUTE: + { + if (g_MuteType[target] == bTime || g_MuteType[target] == bPerm) + continue; + } + case TYPE_UNGAG: + { + if (g_GagType[target] == bTime || g_GagType[target] == bPerm) + continue; + } + case TYPE_UNSILENCE: + { + if ((g_MuteType[target] == bTime || g_MuteType[target] == bPerm) && + (g_GagType[target] == bTime || g_GagType[target] == bPerm)) + continue; + } + } + + new Handle:dataPack = CreateDataPack(); + WritePackCell(dataPack, GetClientUserId2(client)); + WritePackCell(dataPack, GetClientUserId(target)); + WritePackCell(dataPack, type); + WritePackString(dataPack, adminAuth); + WritePackString(dataPack, targetAuth); // not in use in this case + WritePackString(dataPack, reason); + + TempUnBlock(dataPack); + } + + #if defined DEBUG + PrintToServer("Showing activity to server in ProcessUnBlock for targets_count > 1"); + #endif + ShowActivityToServer(client, type + TYPE_TEMP_SHIFT, _, _, target_name, tn_is_ml); + } + else + { + decl String:typeWHERE[100]; + new target = target_list[0]; + + if (IsClientInGame(target)) + { + GetClientAuthId(target, AuthId_Steam2, targetAuth, sizeof(targetAuth)); + } + else + { + return; + } + + switch (type) + { + case TYPE_UNMUTE: + { + if (!BaseComm_IsClientMuted(target)) + { + ReplyToCommand(client, "%s%t", PREFIX, "Player not muted"); + return; + } + else + FormatEx(typeWHERE, sizeof(typeWHERE), "c.type = '%d'", TYPE_MUTE); + } + //------------------------------------------------------------------------------------------------- + case TYPE_UNGAG: + { + if (!BaseComm_IsClientGagged(target)) + { + ReplyToCommand(client, "%s%t", PREFIX, "Player not gagged"); + return; + } + else + FormatEx(typeWHERE, sizeof(typeWHERE), "c.type = '%d'", TYPE_GAG); + } + //------------------------------------------------------------------------------------------------- + case TYPE_UNSILENCE: + { + if (!BaseComm_IsClientMuted(target) || !BaseComm_IsClientGagged(target)) + { + ReplyToCommand(client, "%s%t", PREFIX, "Player not silenced"); + return; + } + else + FormatEx(typeWHERE, sizeof(typeWHERE), "(c.type = '%d' OR c.type = '%d')", TYPE_MUTE, TYPE_GAG); + } + } + + // Pack everything into a data pack so we can retain it + new Handle:dataPack = CreateDataPack(); + WritePackCell(dataPack, GetClientUserId2(client)); + WritePackCell(dataPack, GetClientUserId(target)); + WritePackCell(dataPack, type); + WritePackString(dataPack, adminAuth); + WritePackString(dataPack, targetAuth); + WritePackString(dataPack, reason); + + // Check current player status. If player has temporary punishment - don't get info from DB + if (DB_Connect()) + { + decl String:sAdminAuthEscaped[sizeof(adminAuth) * 2 + 1]; + decl String:sAdminAuthYZEscaped[sizeof(adminAuth) * 2 + 1]; + decl String:sTargetAuthEscaped[sizeof(targetAuth) * 2 + 1]; + decl String:sTargetAuthYZEscaped[sizeof(targetAuth) * 2 + 1]; + + SQL_EscapeString(g_hDatabase, adminAuth, sAdminAuthEscaped, sizeof(sAdminAuthEscaped)); + SQL_EscapeString(g_hDatabase, adminAuth[8], sAdminAuthYZEscaped, sizeof(sAdminAuthYZEscaped)); + SQL_EscapeString(g_hDatabase, targetAuth, sTargetAuthEscaped, sizeof(sTargetAuthEscaped)); + SQL_EscapeString(g_hDatabase, targetAuth[8], sTargetAuthYZEscaped, sizeof(sTargetAuthYZEscaped)); + + decl String:query[4096]; + Format(query, sizeof(query), + "SELECT c.bid, \ + IFNULL((SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), '0') as iaid, \ + c.aid, \ + IF (a.immunity>=g.immunity, a.immunity, IFNULL(g.immunity,0)) as immunity, \ + c.type \ + FROM %s_comms AS c \ + LEFT JOIN %s_admins AS a ON a.aid = c.aid \ + LEFT JOIN %s_srvgroups AS g ON g.name = a.srv_group \ + WHERE RemoveType IS NULL \ + AND (c.authid = '%s' OR c.authid REGEXP '^STEAM_[0-9]:%s$') \ + AND (length = '0' OR ends > UNIX_TIMESTAMP()) \ + AND %s", + DatabasePrefix, sAdminAuthEscaped, sAdminAuthYZEscaped, DatabasePrefix, DatabasePrefix, DatabasePrefix, sTargetAuthEscaped, sTargetAuthYZEscaped, typeWHERE); + + #if defined LOG_QUERIES + LogToFile(logQuery, "ProcessUnBlock. QUERY: %s", query); + #endif + + SQL_TQuery(g_hDatabase, Query_UnBlockSelect, query, dataPack); + } + else + { + #if defined DEBUG + PrintToServer("Calling TempUnBlock from ProcessUnBlock"); + #endif + + if (TempUnBlock(dataPack)) + ShowActivityToServer(client, type + TYPE_TEMP_SHIFT, _, _, g_sName[target], _); + } + } +} + +stock bool:TempUnBlock(Handle:data) +{ + decl String:adminAuth[30], String:targetAuth[30]; + new String:reason[256]; + ResetPack(data); + new adminUserID = ReadPackCell(data); + new targetUserID = ReadPackCell(data); + new type = ReadPackCell(data); + ReadPackString(data, adminAuth, sizeof(adminAuth)); + ReadPackString(data, targetAuth, sizeof(targetAuth)); + ReadPackString(data, reason, sizeof(reason)); + CloseHandle(data); // Need to close datapack + + #if defined DEBUG + PrintToServer("TempUnBlock(adminUID: %d, targetUID: %d, type: %d, adminAuth: %s, targetAuth: %s, reason: %s)", adminUserID, targetUserID, type, adminAuth, targetAuth, reason); + #endif + + new admin = GetClientOfUserId(adminUserID); + new target = GetClientOfUserId(targetUserID); + if (!target) + return false; // target has gone away + + new AdmImmunity = GetAdmImmunity(admin); + new bool:AdmImCheck = (DisUBImCheck == 0 + && ((type == TYPE_UNMUTE && AdmImmunity >= g_iMuteLevel[target]) + || (type == TYPE_UNGAG && AdmImmunity >= g_iGagLevel[target]) + || (type == TYPE_UNSILENCE && AdmImmunity >= g_iMuteLevel[target] + && AdmImmunity >= g_iGagLevel[target]) + ) + ); + + #if defined DEBUG + PrintToServer("WHO WE ARE CHECKING!"); + if (!admin) + PrintToServer("we are console (possibly)"); + if (AdmHasFlag(admin)) + PrintToServer("we have special flag"); + #endif + + // Check access for unblock without db changes (temporary unblock) + new bool:bHasPermission = (!admin && StrEqual(adminAuth, "STEAM_ID_SERVER")) || AdmHasFlag(admin) || AdmImCheck; + // can, if we are console or have special flag. else - deep checking by issuer authid + if (!bHasPermission) { + switch (type) + { + case TYPE_UNMUTE: + { + bHasPermission = StrEqual(adminAuth, g_sMuteAdminAuth[target]); + } + case TYPE_UNGAG: + { + bHasPermission = StrEqual(adminAuth, g_sGagAdminAuth[target]); + } + case TYPE_UNSILENCE: + { + bHasPermission = StrEqual(adminAuth, g_sMuteAdminAuth[target]) && StrEqual(adminAuth, g_sGagAdminAuth[target]); + } + } + } + + if (bHasPermission) + { + switch (type) + { + case TYPE_UNMUTE: + { + PerformUnMute(target); + LogAction(admin, target, "\"%L\" temporary unmuted \"%L\" (reason \"%s\")", admin, target, reason); + } + //------------------------------------------------------------------------------------------------- + case TYPE_UNGAG: + { + PerformUnGag(target); + LogAction(admin, target, "\"%L\" temporary ungagged \"%L\" (reason \"%s\")", admin, target, reason); + } + //------------------------------------------------------------------------------------------------- + case TYPE_UNSILENCE: + { + PerformUnMute(target); + PerformUnGag(target); + LogAction(admin, target, "\"%L\" temporary unsilenced \"%L\" (reason \"%s\")", admin, target, reason); + } + default: + { + return false; + } + } + return true; + } + else + { + if (admin && IsClientInGame(admin)) + { + PrintToChat(admin, "%s%t", PREFIX, "No db error unlock perm"); + PrintToConsole(admin, "%s%t", PREFIX, "No db error unlock perm"); + } + return false; + } +} + +stock InsertTempBlock(length, type, const String:name[], const String:auth[], const String:reason[], const String:adminAuth[], const String:adminIp[]) +{ + LogMessage("Saving punishment for %s into queue", auth); + + decl String:banName[MAX_NAME_LENGTH * 2 + 1]; + decl String:banReason[256 * 2 + 1]; + decl String:sAuthEscaped[64 * 2 + 1]; + decl String:sAdminAuthEscaped[64 * 2 + 1]; + decl String:sQuery[4096], String:sQueryVal[2048]; + new String:sQueryMute[2048], String:sQueryGag[2048]; + + // escaping everything + SQL_EscapeString(SQLiteDB, name, banName, sizeof(banName)); + SQL_EscapeString(SQLiteDB, reason, banReason, sizeof(banReason)); + SQL_EscapeString(SQLiteDB, auth, sAuthEscaped, sizeof(sAuthEscaped)); + SQL_EscapeString(SQLiteDB, adminAuth, sAdminAuthEscaped, sizeof(sAdminAuthEscaped)); + + // steam_id time start_time reason name admin_id admin_ip + FormatEx(sQueryVal, sizeof(sQueryVal), + "'%s', %d, %d, '%s', '%s', '%s', '%s'", + sAuthEscaped, length, GetTime(), banReason, banName, sAdminAuthEscaped, adminIp); + + switch (type) + { + case TYPE_MUTE:FormatEx(sQueryMute, sizeof(sQueryMute), "(%s, %d)", sQueryVal, type); + case TYPE_GAG:FormatEx(sQueryGag, sizeof(sQueryGag), "(%s, %d)", sQueryVal, type); + case TYPE_SILENCE: + { + FormatEx(sQueryMute, sizeof(sQueryMute), "(%s, %d)", sQueryVal, TYPE_MUTE); + FormatEx(sQueryGag, sizeof(sQueryGag), "(%s, %d)", sQueryVal, TYPE_GAG); + } + } + + FormatEx(sQuery, sizeof(sQuery), + "INSERT INTO queue2 (steam_id, time, start_time, reason, name, admin_id, admin_ip, type) VALUES %s%s%s", + sQueryMute, type == TYPE_SILENCE ? ", " : "", sQueryGag); + + #if defined LOG_QUERIES + LogToFile(logQuery, "InsertTempBlock. QUERY: %s", sQuery); + #endif + + SQL_TQuery(SQLiteDB, Query_ErrorCheck, sQuery); +} + +stock ServerInfo() +{ + decl pieces[4]; + new longip = GetConVarInt(CvarHostIp); + pieces[0] = (longip >> 24) & 0x000000FF; + pieces[1] = (longip >> 16) & 0x000000FF; + pieces[2] = (longip >> 8) & 0x000000FF; + pieces[3] = longip & 0x000000FF; + FormatEx(ServerIp, sizeof(ServerIp), "%d.%d.%d.%d", pieces[0], pieces[1], pieces[2], pieces[3]); + GetConVarString(CvarPort, ServerPort, sizeof(ServerPort)); +} + +stock ReadConfig() +{ + InitializeConfigParser(); + + if (ConfigParser == INVALID_HANDLE) + { + return; + } + + decl String:ConfigFile1[PLATFORM_MAX_PATH], String:ConfigFile2[PLATFORM_MAX_PATH]; + BuildPath(Path_SM, ConfigFile1, sizeof(ConfigFile1), "configs/sourcebans/sourcebans.cfg"); + BuildPath(Path_SM, ConfigFile2, sizeof(ConfigFile2), "configs/sourcebans/sourcecomms.cfg"); + + if (FileExists(ConfigFile1)) + { + PrintToServer("%sLoading configs/sourcebans/sourcebans.cfg config file", PREFIX); + InternalReadConfig(ConfigFile1); + } + else + { + SetFailState("FATAL *** ERROR *** can't find %s", ConfigFile1); + } + if (FileExists(ConfigFile2)) + { + PrintToServer("%sLoading configs/sourcecomms.cfg config file", PREFIX); + iNumReasons = 0; + iNumTimes = 0; + InternalReadConfig(ConfigFile2); + if (iNumReasons) + iNumReasons--; + if (iNumTimes) + iNumTimes--; + if (serverID == 0) + { + LogError("You must set valid `ServerID` value in sourcebans.cfg!"); + if (ConfigWhiteListOnly) + { + LogError("ServersWhiteList feature disabled!"); + ConfigWhiteListOnly = 0; + } + } + } + else + { + SetFailState("FATAL *** ERROR *** can't find %s", ConfigFile2); + } + #if defined DEBUG + PrintToServer("Loaded DefaultTime value: %d", DefaultTime); + PrintToServer("Loaded DisableUnblockImmunityCheck value: %d", DisUBImCheck); + #endif +} + + +// some more + +AdminMenu_GetPunishPhrase(client, target, String:name[], length) +{ + decl String:Buffer[192]; + if (g_MuteType[target] > bNot && g_GagType[target] > bNot) + Format(Buffer, sizeof(Buffer), "%T", "AdminMenu_Display_Silenced", client, name); + else if (g_MuteType[target] > bNot) + Format(Buffer, sizeof(Buffer), "%T", "AdminMenu_Display_Muted", client, name); + else if (g_GagType[target] > bNot) + Format(Buffer, sizeof(Buffer), "%T", "AdminMenu_Display_Gagged", client, name); + else + Format(Buffer, sizeof(Buffer), "%T", "AdminMenu_Display_None", client, name); + + strcopy(name, length, Buffer); +} + +bool:Bool_ValidMenuTarget(client, target) +{ + if (target <= 0) + { + if (client) + PrintToChat(client, "%s%t", PREFIX, "AdminMenu_Not_Available"); + else + ReplyToCommand(client, "%s%t", PREFIX, "AdminMenu_Not_Available"); + + return false; + } + else if (!CanUserTarget(client, target)) + { + if (client) + PrintToChat(client, "%s%t", PREFIX, "Command_Target_Not_Targetable"); + else + ReplyToCommand(client, "%s%t", PREFIX, "Command_Target_Not_Targetable"); + + return false; + } + + return true; +} + +stock bool:IsAllowedBlockLength(admin, length, target_count = 1) +{ + if (target_count == 1) + { + // Restriction disabled, all allowed for console, all allowed for admins with special flag + if (!ConfigMaxLength || !admin || AdmHasFlag(admin)) + return true; + + //return false if one of these statements evaluates to true; otherwise, return true + return !(!length || length > ConfigMaxLength); + } + else + { + if (length < 0) //'session punishments allowed for mass-targeting' + return true; + + //return false if one of these statements evaluates to true; otherwise, return true + return !(!length || length > MAX_TIME_MULTI || length > DefaultTime); + } +} + +stock bool:AdmHasFlag(admin) +{ + return admin && CheckCommandAccess(admin, "", UNBLOCK_FLAG, true); +} + +stock _:GetAdmImmunity(admin) +{ + return admin > 0 && GetUserAdmin(admin) != INVALID_ADMIN_ID ? + GetAdminImmunityLevel(GetUserAdmin(admin)) : 0; +} + +stock _:GetClientUserId2(client) +{ + return client ? GetClientUserId(client) : 0; // 0 is for CONSOLE +} + +stock ForcePlayersRecheck() +{ + for (new i = 1; i <= MaxClients; i++) + { + if (IsClientInGame(i) && IsClientAuthorized(i) && !IsFakeClient(i) && g_hPlayerRecheck[i] == INVALID_HANDLE) + { + #if defined DEBUG + { + decl String:clientAuth[64]; + GetClientAuthId(i, AuthId_Steam2, clientAuth, sizeof(clientAuth)); + PrintToServer("Creating Recheck timer for %s", clientAuth); + } + #endif + g_hPlayerRecheck[i] = CreateTimer(float(i), ClientRecheck, GetClientUserId(i)); + } + } +} + +stock bool:NotApplyToThisServer(srvID) +{ + return ConfigWhiteListOnly && FindValueInArray(g_hServersWhiteList, srvID) == -1; +} + +stock MarkClientAsUnMuted(target) +{ + g_MuteType[target] = bNot; + g_iMuteTime[target] = 0; + g_iMuteLength[target] = 0; + g_iMuteLevel[target] = -1; + g_sMuteAdminName[target][0] = '\0'; + g_sMuteReason[target][0] = '\0'; + g_sMuteAdminAuth[target][0] = '\0'; +} + +stock MarkClientAsUnGagged(target) +{ + g_GagType[target] = bNot; + g_iGagTime[target] = 0; + g_iGagLength[target] = 0; + g_iGagLevel[target] = -1; + g_sGagAdminName[target][0] = '\0'; + g_sGagReason[target][0] = '\0'; + g_sGagAdminAuth[target][0] = '\0'; +} + +stock MarkClientAsMuted(target, time = NOW, length = -1, const String:adminName[] = "CONSOLE", const String:adminAuth[] = "STEAM_ID_SERVER", adminImmunity = 0, const String:reason[] = "") +{ + if (time) + g_iMuteTime[target] = time; + else + g_iMuteTime[target] = GetTime(); + + g_iMuteLength[target] = length; + g_iMuteLevel[target] = adminImmunity ? adminImmunity : ConsoleImmunity; + strcopy(g_sMuteAdminName[target], sizeof(g_sMuteAdminName[]), adminName); + strcopy(g_sMuteReason[target], sizeof(g_sMuteReason[]), reason); + strcopy(g_sMuteAdminAuth[target], sizeof(g_sMuteAdminAuth[]), adminAuth); + + if (length > 0) + g_MuteType[target] = bTime; + else if (length == 0) + g_MuteType[target] = bPerm; + else + g_MuteType[target] = bSess; +} + +stock MarkClientAsGagged(target, time = NOW, length = -1, const String:adminName[] = "CONSOLE", const String:adminAuth[] = "STEAM_ID_SERVER", adminImmunity = 0, const String:reason[] = "") +{ + if (time) + g_iGagTime[target] = time; + else + g_iGagTime[target] = GetTime(); + + g_iGagLength[target] = length; + g_iGagLevel[target] = adminImmunity ? adminImmunity : ConsoleImmunity; + strcopy(g_sGagAdminName[target], sizeof(g_sGagAdminName[]), adminName); + strcopy(g_sGagReason[target], sizeof(g_sGagReason[]), reason); + strcopy(g_sGagAdminAuth[target], sizeof(g_sGagAdminAuth[]), adminAuth); + + if (length > 0) + g_GagType[target] = bTime; + else if (length == 0) + g_GagType[target] = bPerm; + else + g_GagType[target] = bSess; +} + +stock CloseMuteExpireTimer(target) +{ + if (g_hMuteExpireTimer[target] != INVALID_HANDLE && CloseHandle(g_hMuteExpireTimer[target])) + g_hMuteExpireTimer[target] = INVALID_HANDLE; +} + +stock CloseGagExpireTimer(target) +{ + if (g_hGagExpireTimer[target] != INVALID_HANDLE && CloseHandle(g_hGagExpireTimer[target])) + g_hGagExpireTimer[target] = INVALID_HANDLE; +} + +stock CreateMuteExpireTimer(target, remainingTime = 0) +{ + if (g_iMuteLength[target] > 0) + { + new Handle:hPack; + + if (remainingTime) + g_hMuteExpireTimer[target] = CreateDataTimer(float(remainingTime), Timer_MuteExpire, hPack, TIMER_FLAG_NO_MAPCHANGE); + else + g_hMuteExpireTimer[target] = CreateDataTimer(float(g_iMuteLength[target] * 60), Timer_MuteExpire, hPack, TIMER_FLAG_NO_MAPCHANGE); + + WritePackCell(hPack, target); + WritePackCell(hPack, GetClientUserId(target)); + } +} + +stock CreateGagExpireTimer(target, remainingTime = 0) +{ + if (g_iGagLength[target] > 0) + { + new Handle:hPack; + + if (remainingTime) + g_hGagExpireTimer[target] = CreateDataTimer(float(remainingTime), Timer_GagExpire, hPack, TIMER_FLAG_NO_MAPCHANGE); + else + g_hGagExpireTimer[target] = CreateDataTimer(float(g_iGagLength[target] * 60), Timer_GagExpire, hPack, TIMER_FLAG_NO_MAPCHANGE); + + WritePackCell(hPack, target); + WritePackCell(hPack, GetClientUserId(target)); + } +} + +stock PerformUnMute(target) +{ + MarkClientAsUnMuted(target); + BaseComm_SetClientMute(target, false); + CloseMuteExpireTimer(target); +} + +stock PerformUnGag(target) +{ + MarkClientAsUnGagged(target); + BaseComm_SetClientGag(target, false); + CloseGagExpireTimer(target); +} + +stock PerformMute(target, time = NOW, length = -1, const String:adminName[] = "CONSOLE", const String:adminAuth[] = "STEAM_ID_SERVER", adminImmunity = 0, const String:reason[] = "", remaining_time = 0) +{ + MarkClientAsMuted(target, time, length, adminName, adminAuth, adminImmunity, reason); + BaseComm_SetClientMute(target, true); + CreateMuteExpireTimer(target, remaining_time); +} + +stock PerformGag(target, time = NOW, length = -1, const String:adminName[] = "CONSOLE", const String:adminAuth[] = "STEAM_ID_SERVER", adminImmunity = 0, const String:reason[] = "", remaining_time = 0) +{ + MarkClientAsGagged(target, time, length, adminName, adminAuth, adminImmunity, reason); + BaseComm_SetClientGag(target, true); + CreateGagExpireTimer(target, remaining_time); +} + +stock SavePunishment(admin = 0, target, type, length = -1, const String:reason[] = "") +{ + if (type < TYPE_MUTE || type > TYPE_SILENCE) + return; + + // target information + decl String:targetAuth[64]; + if (IsClientInGame(target)) + { + GetClientAuthId(target, AuthId_Steam2, targetAuth, sizeof(targetAuth)); + } + else + { + return; + } + + decl String:adminIp[24]; + decl String:adminAuth[64]; + if (admin && IsClientInGame(admin)) + { + GetClientIP(admin, adminIp, sizeof(adminIp)); + GetClientAuthId(admin, AuthId_Steam2, adminAuth, sizeof(adminAuth)); + } + else + { + // setup dummy adminAuth and adminIp for server + strcopy(adminAuth, sizeof(adminAuth), "STEAM_ID_SERVER"); + strcopy(adminIp, sizeof(adminIp), ServerIp); + } + + decl String:sName[MAX_NAME_LENGTH]; + strcopy(sName, sizeof(sName), g_sName[target]); + + if (DB_Connect()) + { + // Accepts length in minutes, writes to db in seconds! In all over places in plugin - length is in minutes. + decl String:banName[MAX_NAME_LENGTH * 2 + 1]; + decl String:banReason[256 * 2 + 1]; + decl String:sAuthidEscaped[64 * 2 + 1]; + decl String:sAdminAuthIdEscaped[64 * 2 + 1]; + decl String:sAdminAuthIdYZEscaped[64 * 2 + 1]; + decl String:sQuery[4096], String:sQueryAdm[512], String:sQueryVal[1024]; + decl String:sQueryMute[1024], String:sQueryGag[1024]; + sQueryMute[0] = 0; + sQueryGag[0] = 0; + + // escaping everything + SQL_EscapeString(g_hDatabase, sName, banName, sizeof(banName)); + SQL_EscapeString(g_hDatabase, reason, banReason, sizeof(banReason)); + SQL_EscapeString(g_hDatabase, targetAuth, sAuthidEscaped, sizeof(sAuthidEscaped)); + SQL_EscapeString(g_hDatabase, adminAuth, sAdminAuthIdEscaped, sizeof(sAdminAuthIdEscaped)); + SQL_EscapeString(g_hDatabase, adminAuth[8], sAdminAuthIdYZEscaped, sizeof(sAdminAuthIdYZEscaped)); + + // bid authid name created ends lenght reason aid adminip sid removedBy removedType removedon type ureason + FormatEx(sQueryAdm, sizeof(sQueryAdm), + "IFNULL((SELECT aid FROM %s_admins WHERE authid = '%s' OR authid REGEXP '^STEAM_[0-9]:%s$'), 0)", + DatabasePrefix, sAdminAuthIdEscaped, sAdminAuthIdYZEscaped); + + if (length >= 0) + { + // authid name, created, ends, length, reason, aid, adminIp, sid + FormatEx(sQueryVal, sizeof(sQueryVal), + "'%s', '%s', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', %s, '%s', %d", + sAuthidEscaped, banName, length * 60, length * 60, banReason, sQueryAdm, adminIp, serverID); + } + else // Session mutes + { + // authid name, created, ends, length, reason, aid, adminIp, sid + FormatEx(sQueryVal, sizeof(sQueryVal), + "'%s', '%s', UNIX_TIMESTAMP(), UNIX_TIMESTAMP() + %d, %d, '%s', %s, '%s', %d", + sAuthidEscaped, banName, SESSION_MUTE_FALLBACK, -1, banReason, sQueryAdm, adminIp, serverID); + } + + switch (type) + { + case TYPE_GAG:FormatEx(sQueryGag, sizeof(sQueryGag), "(%s, %d)", sQueryVal, type); + case TYPE_MUTE:FormatEx(sQueryMute, sizeof(sQueryMute), "(%s, %d)", sQueryVal, type); + case TYPE_SILENCE: + { + FormatEx(sQueryMute, sizeof(sQueryMute), "(%s, %d)", sQueryVal, TYPE_MUTE); + FormatEx(sQueryGag, sizeof(sQueryGag), "(%s, %d)", sQueryVal, TYPE_GAG); + } + } + + // litle magic - one query for all actions (mute, gag or silence) + FormatEx(sQuery, sizeof(sQuery), + "INSERT INTO %s_comms (authid, name, created, ends, length, reason, aid, adminIp, sid, type) VALUES %s%s%s", + DatabasePrefix, sQueryMute, type == TYPE_SILENCE ? ", " : "", sQueryGag); + + #if defined LOG_QUERIES + LogToFile(logQuery, "SavePunishment. QUERY: %s", sQuery); + #endif + + // all data cached before calling asynchronous functions + new Handle:dataPack = CreateDataPack(); + WritePackCell(dataPack, admin > 0 ? GetClientUserId(admin) : 0); + WritePackCell(dataPack, GetClientUserId(target)); + WritePackCell(dataPack, length); + WritePackCell(dataPack, type); + WritePackString(dataPack, reason); + WritePackString(dataPack, sName); + WritePackString(dataPack, targetAuth); + WritePackString(dataPack, adminAuth); + WritePackString(dataPack, adminIp); + + SQL_TQuery(g_hDatabase, Query_AddBlockInsert, sQuery, dataPack, DBPrio_High); + } + else + InsertTempBlock(length, type, sName, targetAuth, reason, adminAuth, adminIp); +} + +stock ShowActivityToServer(admin, type, length = 0, String:reason[] = "", String:targetName[], bool:ml = false) +{ + #if defined DEBUG + PrintToServer("ShowActivityToServer(admin: %d, type: %d, length: %d, reason: %s, name: %s, ml: %b", + admin, type, length, reason, targetName, ml); + #endif + + decl String:actionName[32], String:translationName[64]; + switch (type) + { + case TYPE_MUTE: + { + if (length > 0) + strcopy(actionName, sizeof(actionName), "Muted"); + else if (length == 0) + strcopy(actionName, sizeof(actionName), "Permamuted"); + else // temp block + strcopy(actionName, sizeof(actionName), "Temp muted"); + } + //------------------------------------------------------------------------------------------------- + case TYPE_GAG: + { + if (length > 0) + strcopy(actionName, sizeof(actionName), "Gagged"); + else if (length == 0) + strcopy(actionName, sizeof(actionName), "Permagagged"); + else //temp block + strcopy(actionName, sizeof(actionName), "Temp gagged"); + } + //------------------------------------------------------------------------------------------------- + case TYPE_SILENCE: + { + if (length > 0) + strcopy(actionName, sizeof(actionName), "Silenced"); + else if (length == 0) + strcopy(actionName, sizeof(actionName), "Permasilenced"); + else //temp block + strcopy(actionName, sizeof(actionName), "Temp silenced"); + } + //------------------------------------------------------------------------------------------------- + case TYPE_UNMUTE: + { + strcopy(actionName, sizeof(actionName), "Unmuted"); + } + //------------------------------------------------------------------------------------------------- + case TYPE_UNGAG: + { + strcopy(actionName, sizeof(actionName), "Ungagged"); + } + //------------------------------------------------------------------------------------------------- + case TYPE_TEMP_UNMUTE: + { + strcopy(actionName, sizeof(actionName), "Temp unmuted"); + } + //------------------------------------------------------------------------------------------------- + case TYPE_TEMP_UNGAG: + { + strcopy(actionName, sizeof(actionName), "Temp ungagged"); + } + //------------------------------------------------------------------------------------------------- + case TYPE_TEMP_UNSILENCE: + { + strcopy(actionName, sizeof(actionName), "Temp unsilenced"); + } + //------------------------------------------------------------------------------------------------- + default: + { + return; + } + } + + Format(translationName, sizeof(translationName), "%s %s", actionName, reason[0] == '\0' ? "player" : "player reason"); + #if defined DEBUG + PrintToServer("translation name: %s", translationName); + #endif + + if (length > 0) + { + if (ml) + ShowActivity2(admin, PREFIX, "%t", translationName, targetName, length, reason); + else + ShowActivity2(admin, PREFIX, "%t", translationName, "_s", targetName, length, reason); + } + else + { + if (ml) + ShowActivity2(admin, PREFIX, "%t", translationName, targetName, reason); + else + ShowActivity2(admin, PREFIX, "%t", translationName, "_s", targetName, reason); + } +} + +// Natives // +public Native_SetClientMute(Handle hPlugin, int numParams) +{ + int target = GetNativeCell(1); + if (target < 1 || target > MaxClients) + { + ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", target); + return false; + } + + if (!IsClientInGame(target)) + { + ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not in game", target); + return false; + } + + bool muteState = bool:GetNativeCell(2); + int muteLength = GetNativeCell(3); + + if (muteState && muteLength == 0) + { + ThrowNativeError(SP_ERROR_NATIVE, "Permanent mute is not allowed!"); + return false; + } + + bool bSaveToDB = bool:GetNativeCell(4); + if (!muteState && bSaveToDB) + { + ThrowNativeError(SP_ERROR_NATIVE, "Removing punishments from DB is not allowed!"); + return false; + } + + char sReason[256]; + GetNativeString(5, sReason, sizeof(sReason)); + + if (muteState) + { + if (g_MuteType[target] > bNot) + { + return false; + } + + PerformMute(target, _, muteLength, _, _, _, sReason); + if (bSaveToDB) + SavePunishment(_, target, TYPE_MUTE, muteLength, sReason); + } + else + { + if (g_MuteType[target] == bNot) + { + return false; + } + + PerformUnMute(target); + } + + return true; +} + +public Native_SetClientGag(Handle:hPlugin, numParams) +{ + new target = GetNativeCell(1); + if (target < 1 || target > MaxClients) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", target); + } + + if (!IsClientInGame(target)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not in game", target); + } + + new bool:gagState = GetNativeCell(2); + new gagLength = GetNativeCell(3); + if (gagState && gagLength == 0) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Permanent gag is not allowed!"); + } + + new bool:bSaveToDB = GetNativeCell(4); + if (!gagState && bSaveToDB) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Removing punishments from DB is not allowed!"); + } + + new String:sReason[256]; + GetNativeString(5, sReason, sizeof(sReason)); + + if (gagState) + { + if (g_GagType[target] > bNot) + { + return false; + } + + PerformGag(target, _, gagLength, _, _, _, sReason); + + if (bSaveToDB) + SavePunishment(_, target, TYPE_GAG, gagLength, sReason); + } + else + { + if (g_GagType[target] == bNot) + { + return false; + } + + PerformUnGag(target); + } + + return true; +} + +public Native_GetClientMuteType(Handle:hPlugin, numParams) +{ + new target = GetNativeCell(1); + if (target < 1 || target > MaxClients) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", target); + } + + if (!IsClientInGame(target)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not in game", target); + } + + return bType:g_MuteType[target]; +} + +public Native_GetClientGagType(Handle:hPlugin, numParams) +{ + new target = GetNativeCell(1); + if (target < 1 || target > MaxClients) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Invalid client index %d", target); + } + + if (!IsClientInGame(target)) + { + return ThrowNativeError(SP_ERROR_NATIVE, "Client %d is not in game", target); + } + + return bType:g_GagType[target]; +} +//Yarr! diff --git a/Sourcebans++/scripting/sbpp_main.sp b/Sourcebans++/scripting/sbpp_main.sp new file mode 100644 index 0000000..3e62ca1 --- /dev/null +++ b/Sourcebans++/scripting/sbpp_main.sp @@ -0,0 +1,2750 @@ +// ************************************************************************* +// This file is part of SourceBans++. +// +// Copyright (C) 2014-2016 SourceBans++ Dev Team +// +// SourceBans++ is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, per version 3 of the License. +// +// SourceBans++ is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with SourceBans++. If not, see . +// +// This file is based off work(s) covered by the following copyright(s): +// +// SourceBans 1.4.11 +// Copyright (C) 2007-2015 SourceBans Team - Part of GameConnect +// Licensed under GNU GPL version 3, or later. +// Page: - +// +// ************************************************************************* + +#pragma semicolon 1 +#include +#include + +#undef REQUIRE_PLUGIN +#include +#tryinclude + +#define SB_VERSION "1.6.4" +#define SBR_VERSION "1.6.4" + +#if defined _updater_included +#define UPDATE_URL "https://sbpp.github.io/updater/updatefile.txt" +#endif + +//GLOBAL DEFINES +#define YELLOW 0x01 +#define NAMECOLOR 0x02 +#define TEAMCOLOR 0x03 +#define GREEN 0x04 + +#define DISABLE_ADDBAN 1 +#define DISABLE_UNBAN 2 + +#define FLAG_LETTERS_SIZE 26 + +//#define DEBUG + +enum State/* ConfigState */ +{ + ConfigStateNone = 0, + ConfigStateConfig, + ConfigStateReasons, + ConfigStateHacking, + ConfigStateTime +} + +State ConfigState; + +#define Prefix "[SourceBans++] " + +/* Admin Stuff*/ +AdminCachePart loadPart; + +AdminFlag g_FlagLetters[FLAG_LETTERS_SIZE]; + +/* Cvar handle*/ +ConVar + CvarHostIp + , CvarPort; + +/* Database handle */ +Database DB; +Database SQLiteDB; + +char + ServerIp[24] + , ServerPort[7] + , DatabasePrefix[10] = "sb" + , WebsiteAddress[128] + , groupsLoc[128] /* Admin KeyValues */ + , adminsLoc[128] + , overridesLoc[128] + , logFile[256]; /* Log Stuff */ + +float RetryTime = 15.0; + +bool + loadAdmins /* Admin Stuff*/ + , loadGroups + , loadOverrides + , LateLoaded + , AutoAdd + , g_bConnecting = false + , requireSiteLogin = false /* Require a lastvisited from SB site */ + , backupConfig = true + , enableAdmins = true + , PlayerStatus[MAXPLAYERS + 1]; /* Player ban check status */ + +int + g_BanTarget[MAXPLAYERS + 1] = { -1, ... } + , g_BanTime[MAXPLAYERS + 1] = { -1, ... } + , curLoading + , serverID = -1 + , ProcessQueueTime = 5 + , g_ownReasons[MAXPLAYERS + 1] = { false, ... } /* Own Chat Reason */ + , CommandDisable; /* Disable of addban and unban */ + +Handle + ConfigParser + , hTopMenu = INVALID_HANDLE + , TimeMenuHandle /* Menu file globals */ + , ReasonMenuHandle + , HackingMenuHandle + , g_hFwd_OnBanAdded + , g_hFwd_OnReportAdded + , PlayerRecheck[MAXPLAYERS + 1] = { INVALID_HANDLE, ... } /* Datapack and Timer handles */ + , PlayerDataPack[MAXPLAYERS + 1] = { INVALID_HANDLE, ... }; + +public Plugin myinfo = +{ + name = "SourceBans++: Main Plugin", + author = "SourceBans Development Team, SourceBans++ Dev Team", + description = "Advanced ban management for the Source engine", + version = SBR_VERSION, + url = "https://sbpp.github.io" +}; + +#if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 +public APLRes AskPluginLoad2(Handle myself, bool late, char[] error, int err_max) +#else +public bool AskPluginLoad(Handle myself, bool late, char[] error, int err_max) +#endif +{ + RegPluginLibrary("sourcebans++"); + + CreateNative("SBBanPlayer", Native_SBBanPlayer); + CreateNative("SBPP_BanPlayer", Native_SBBanPlayer); + CreateNative("SBPP_ReportPlayer", Native_SBReportPlayer); + + g_hFwd_OnBanAdded = CreateGlobalForward("SBPP_OnBanPlayer", ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_String); + g_hFwd_OnReportAdded = CreateGlobalForward("SBPP_OnReportPlayer", ET_Ignore, Param_Cell, Param_Cell, Param_String); + + LateLoaded = late; + + #if SOURCEMOD_V_MAJOR >= 1 && SOURCEMOD_V_MINOR >= 3 + return APLRes_Success; + #else + return true; + #endif +} + +public OnPluginStart() +{ + LoadTranslations("common.phrases"); + LoadTranslations("plugin.basecommands"); + LoadTranslations("sourcebans.phrases"); + LoadTranslations("basebans.phrases"); + loadAdmins = loadGroups = loadOverrides = false; + + CvarHostIp = FindConVar("hostip"); + CvarPort = FindConVar("hostport"); + CreateConVar("sb_version", SB_VERSION, _, FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY); + CreateConVar("sbr_version", SBR_VERSION, _, FCVAR_SPONLY | FCVAR_REPLICATED | FCVAR_NOTIFY); + RegServerCmd("sm_rehash", sm_rehash, "Reload SQL admins"); + RegAdminCmd("sm_ban", CommandBan, ADMFLAG_BAN, "sm_ban <#userid|name> [reason]", "sourcebans"); + RegAdminCmd("sm_banip", CommandBanIp, ADMFLAG_BAN, "sm_banip