", "<", "&", "%", "|", "^", "~", "(", ")"); return str_replace($replacements, "", $input); } } class Admin { public $adminID = -1; public $adminGroupID = -1; public $adminSteamID = ""; public $adminUser = ""; public function getAdminIDFromName($name) { $name = $GLOBALS['SBPP']->real_escape_string($name); $query = "SELECT `aid` FROM `sb_admins` WHERE `user` LIKE '%$name%'"; $queryHndl = $GLOBALS['SBPP']->query($query); if ($queryHndl) { $result = $queryHndl->fetch_assoc(); $queryHndl->free_result(); if ($result) { return $result['aid']; } } else { die(); // Database error } return -1; // No matching admin found } public function GetAdminNameFromSteamID($steamID) { if (!str_contains($steamID, "STEAM")) { return "CONSOLE"; } $sql = "SELECT * FROM `sb_admins` WHERE `authid`=?"; $stmt = $GLOBALS['SBPP']->prepare($sql); $stmt->bind_param("s", $steamID); $stmt->execute(); $queryResult = $stmt->get_result(); $stmt->close(); $results = $queryResult->fetch_all(MYSQLI_ASSOC); foreach ($results as $result) { return $result['user']; } return "Admin Deleted"; } public function IsLoginValid($steamID, $secret_key, $bInitialVerification) { if (empty($steamID) || empty($secret_key) || $secret_key !== $GLOBALS['SECRET_KEY']) { return false; } $sql = "SELECT aid FROM sb_admins WHERE authid = ?"; $stmt = $GLOBALS['SBPP']->prepare($sql); $stmt->bind_param("s", $steamID); $stmt->execute(); $queryResult = $stmt->get_result(); $stmt->close(); // Fetch the result from the query $row = $queryResult->fetch_assoc(); $sbppaid = $row['aid']; // Compare the cookie 'aid' with the result from the query if (!$bInitialVerification && $sbppaid != $_COOKIE['aid']) { return false; } $sql = "SELECT * FROM `sb_admins` WHERE `authid`=?"; $stmt = $GLOBALS['SBPP']->prepare($sql); $stmt->bind_param("s", $steamID); $stmt->execute(); $queryResult = $stmt->get_result(); $stmt->close(); if ($queryResult->num_rows <= 0) { return false; } $acceptableGroups = array_merge(GID_STAFF, GID_ADMIN); $resultsAAA = $queryResult->fetch_all(MYSQLI_ASSOC); foreach ($resultsAAA as $result) { $gid = $result['gid']; if (!in_array($gid, $acceptableGroups) || $gid == -1) { return false; } } return true; } public function UpdateAdminInfo($steamID) { $secret_key = $_COOKIE['secret_key']; if (!$this->IsLoginValid($steamID, $secret_key, false)) { return false; } $sql = "SELECT `aid`, `gid`, `authid`, `user` FROM `sb_admins` WHERE `authid`=?"; $stmt = $GLOBALS['SBPP']->prepare($sql); $stmt->bind_param("s", $steamID); $stmt->execute(); $queryResult = $stmt->get_result(); if ($queryResult->num_rows <= 0) { $stmt->close(); return false; } $result = $queryResult->fetch_assoc(); $stmt->close(); $this->adminID = $result['aid']; $this->adminGroupID = $result['gid']; $this->adminSteamID = $result['authid']; $this->adminUser = $result['user']; return true; } public function DoesHaveFullAccess() { if (!isset($_COOKIE['steamID'])) { return false; } // acceptatable group ids $groups = array(1, 3, 4); if (in_array($this->adminGroupID, $groups)) { return true; } return false; } } class Eban { public function UnbanByID($id, $reasonA) { if (!isset($_COOKIE['steamID'])) { // This should never happen, but just to be safe return false; } if (empty($reasonA)) { $reasonA = "No Reason"; } $reason = Utility::sanitizeInput($reasonA); $admin = new Admin(); $admin->UpdateAdminInfo($_COOKIE['steamID']); $adminName = $admin->adminUser; $adminSteamID = $admin->adminSteamID; $Eban = new Eban(); $resultsB = $Eban->getEbanInfoFromID($id); $playerName = $resultsB['client_name']; $playerSteamID = $resultsB['client_steamid']; $length = $resultsB['duration']; // Single UPDATE: record who unbanned it, mark it inactive. No more // moving rows between tables - EntWatch_Old_Eban is retired. $sql = "UPDATE `EntWatch_Current_Eban` SET `admin_name_unban` = ?, `admin_steamid_unban` = ?, `reason_unban` = ?, `timestamp_unban` = ?, `is_expired` = 1 WHERE `id` = ?"; $stmt = $GLOBALS['DB']->prepare($sql); $time_unban = time(); $stmt->bind_param("sssii", $adminName, $adminSteamID, $reason, $time_unban, $id); $stmt->execute(); $stmt->close(); // Lift the restriction in-game immediately, matching what the // plugin's own ClientUnrestrict() does when it processes this. $this->ZeroRestrictCookies($playerSteamID); $time = time(); // Log the action, but don't let a logging failure block the unban // itself from completing successfully - just record it and move on. $sql = "INSERT INTO `web_logs` (`message`, `admin_name`, `admin_steamid`, `client_name`, `client_steamid`, `time_stamp`) VALUES (?, ?, ?, ?, ?, ?)"; $message = "Eban Removed (was $length minutes. Reason: $reason)"; if ($stmt = $GLOBALS['DB']->prepare($sql)) { $stmt->bind_param("sssssi", $message, $adminName, $adminSteamID, $playerName, $playerSteamID, $time); $stmt->execute(); $stmt->close(); } else { error_log("web_logs prepare error: " . $GLOBALS['DB']->error); } echo ""; return true; } public function RemoveEbanFromDB($id) { $admin = new Admin(); $adminSteamID = (isset($_COOKIE['steamID']) ? $_COOKIE['steamID'] : ""); $admin->UpdateAdminInfo($adminSteamID); if (!IsAdminLoggedIn() || !$admin->DoesHaveFullAccess()) { return false; } $resultsC = $this->getEbanInfoFromID($id); $playerName = $resultsC['client_name']; $playerSteamID = $resultsC['client_steamid']; $length = $resultsC['duration']; $reason = $resultsC['reason']; $isExpired = ($resultsC['is_expired'] == 1) ? true : false; $isRemoved = ($resultsC['admin_steamid_unban'] != "" && $resultsC['admin_steamid_unban'] != "SERVER") ? true : false; $status = "Active"; if ($isExpired && !$isRemoved) { $status = "Expired"; } if ($isRemoved) { $status = "Removed"; } $message = "Eban Deleted (Player Name: $playerName, Player SteamID: $playerSteamID, was $length minutes. Issued for: $reason. Eban was $status)"; // Use prepared statement for DELETE - only one table now $sql = "DELETE FROM `EntWatch_Current_Eban` WHERE `id` = ?"; $stmt = $GLOBALS['DB']->prepare($sql); $stmt->bind_param("i", $id); $stmt->execute(); $stmt->close(); // Only touch the cookie cache if this row was actually the player's // current active restriction. A client can only have one active ban // at a time - deleting an already-expired/unbanned historical row // must not zero out cookies that may belong to a newer, still-active // restriction for the same player. if (!$isExpired && !$isRemoved) { $this->ZeroRestrictCookies($playerSteamID); } $adminName = $admin->adminUser; $time = time(); // Log the action, but don't let a logging failure block the delete // itself from completing successfully - just record it and move on. $sql = "INSERT INTO `web_logs` (`message`, `admin_name`, `admin_steamid`, `client_name`, `client_steamid`, `time_stamp`) VALUES (?, ?, ?, ?, ?, ?)"; if ($stmt = $GLOBALS['DB']->prepare($sql)) { $stmt->bind_param("sssssi", $message, $adminName, $adminSteamID, $playerName, $playerSteamID, $time); $stmt->execute(); $stmt->close(); } else { error_log("web_logs prepare error: " . $GLOBALS['DB']->error); } echo ""; } public function formatPlaytime($minutes) { $minutes = intval($minutes); $hours = intval($minutes / 60); $mins = $minutes % 60; $hoursPhrase = ($hours == 1) ? "Hour" : "Hours"; $minsPhrase = ($mins == 1) ? "Minute" : "Minutes"; if ($hours <= 0) { return "$mins $minsPhrase"; } if ($mins <= 0) { return "$hours $hoursPhrase"; } return "$hours $hoursPhrase, $mins $minsPhrase"; } // Looks up a player's total accumulated playtime (in minutes) from the // separate playtime-stats database, same source kbans uses. Returns 0 // if the player has no playtime record (e.g. has never connected). public function GetPlaytimeMinutes($steamID) { $stmt = $GLOBALS['DB_PLAYTIME']->prepare( "SELECT SUM(ze_time) AS ze_time_total FROM player_time WHERE steam_id = ? GROUP BY steam_id ORDER BY ze_time_total DESC" ); $stmt->bind_param("s", $steamID); $stmt->execute(); $result = $stmt->get_result(); $row = $result->fetch_assoc(); $stmt->close(); // ze_time is stored in seconds in this table - convert to minutes // here so every caller can keep working in flat minutes. return $row ? intval($row['ze_time_total'] / 60) : 0; } // Pushes Issued/Expire/Length into the sourcemod plugin's own cookie // cache, so the in-game restriction actually reflects what was set via // the web panel. Uses INSERT ... ON DUPLICATE KEY UPDATE since a brand // new player may have none of these cookie rows cached yet at all - a // plain UPDATE would silently touch zero rows in that case. Requires // sm_cookie_cache's standard (player, cookie_id) unique/primary key. public function SyncRestrictCookies($steamID, $lengthInMinutes, $time_played_start, $time_played_end) { $time = time(); if ($lengthInMinutes == 0) { // Permanent - mirrors the plugin's own permanent-ban cookie // behavior: Issued just needs to be a truthy value, and // Expire=0 is the sentinel ClientRestricted() checks for. $issued_hours = $time; $issued_minutes = 0; $expire_hours = 0; $expire_minutes = 0; $length_hours = 0; $length_minutes = 0; } else { $issued_hours = intdiv($time_played_start, 60); $issued_minutes = $time_played_start % 60; $expire_hours = intdiv($time_played_end, 60); $expire_minutes = $time_played_end % 60; $length_hours = intdiv($lengthInMinutes, 60); $length_minutes = $lengthInMinutes % 60; } $cookies = [ 2 => $issued_hours, // EW_RestrictIssued 195721 => $issued_minutes, // EW_RestrictIssued_minutes 3 => $expire_hours, // EW_RestrictExpire 195723 => $expire_minutes, // EW_RestrictExpire_minutes 4 => $length_hours, // EW_RestrictLength 195725 => $length_minutes, // EW_RestrictLength_minutes ]; $sql = "INSERT INTO `sm_cookie_cache` (`player`, `cookie_id`, `value`, `timestamp`) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE `value` = VALUES(`value`), `timestamp` = VALUES(`timestamp`)"; $stmt = $GLOBALS['DB_COOKIES']->prepare($sql); foreach ($cookies as $cookieID => $value) { $valueStr = strval($value); $stmt->bind_param("sisi", $steamID, $cookieID, $valueStr, $time); $stmt->execute(); } $stmt->close(); } // Zeroes out all six EW_Restrict* cookies for a player, matching exactly // what the sourcemod plugin's own ClientUnrestrict() does. Used by // UnbanByID() and RemoveEbanFromDB() so the in-game restriction lifts // immediately, rather than waiting for the plugin's own polling. public function ZeroRestrictCookies($steamID) { $time = time(); $cookieIDs = [2, 195721, 3, 195723, 4, 195725]; $sql = "INSERT INTO `sm_cookie_cache` (`player`, `cookie_id`, `value`, `timestamp`) VALUES (?, ?, '0', ?) ON DUPLICATE KEY UPDATE `value` = '0', `timestamp` = VALUES(`timestamp`)"; $stmt = $GLOBALS['DB_COOKIES']->prepare($sql); foreach ($cookieIDs as $cookieID) { $stmt->bind_param("sii", $steamID, $cookieID, $time); $stmt->execute(); } $stmt->close(); } public function formatLength($seconds) { /* if less than one minute */ if ($seconds == 0) { return "Permanent"; } if ($seconds < 60) { return "$seconds Seconds"; } /* if one minute or more */ if ($seconds >= 60 && $seconds < 3600) { $minutes = ($seconds / 60); $minutesPhrase = ($minutes > 1) ? "Minutes" : "Minute"; return "$minutes $minutesPhrase"; } /* If hour or more*/ if ($seconds >= 3600 && $seconds < 86400) { $hours = intval(($seconds / 3600)); $minutes = intval((($seconds / 60) % 60)); $hoursPhrase = ($hours > 1) ? "Hours" : "Hour"; $minutesPhrase = ($minutes > 1) ? "Minutes" : "Minute"; if ($minutes <= 0) { return "$hours $hoursPhrase"; } return "$hours $hoursPhrase, $minutes $minutesPhrase"; } /* If day or more */ if ($seconds >= 86400 && $seconds < 604800) { $days = intval(($seconds / 86400)); $hours = intval((($seconds / 3600) % 24)); $daysPhrase = ($days > 1) ? "Days" : "Day"; $hoursPhrase = ($hours > 1) ? "Hours" : "Hour"; if ($hours <= 0) { return "$days $daysPhrase"; } return "$days $daysPhrase, $hours $hoursPhrase"; } /* if week or more */ if ($seconds >= 604800 && $seconds < 2592000) { $weeks = intval(($seconds / 604800)); $days = intval((($seconds / 86400) % 7)); $weeksPhrase = ($weeks > 1) ? "Weeks" : "Week"; $daysPhrase = ($days > 1) ? "Days" : "Day"; if ($days <= 0) { return "$weeks $weeksPhrase"; } return "$weeks $weeksPhrase, $days $daysPhrase"; } /* if month or more */ if ($seconds >= 2592000) { $months = intval(($seconds / 2592000)); $days = intval((($seconds / 86400) % 30)); $monthsPhrase = ($months > 1) ? "Months" : "Month"; $daysPhrase = ($days > 1) ? "Days" : "Day"; if ($days <= 0) { return "$months $monthsPhrase"; } return "$months $monthsPhrase, $days $daysPhrase"; } } public function getEbanInfoFromID($id) { $sql = "SELECT * FROM `EntWatch_Current_Eban` WHERE `id`=?"; $stmt = $GLOBALS['DB']->prepare($sql); $stmt->bind_param("i", $id); $stmt->execute(); $query = $stmt->get_result(); $results = $query->fetch_all(MYSQLI_ASSOC); $query->free(); foreach ($results as $result) { return $result; } } public function GetEbansNumber($steamID) { $search = $steamID; $searchMethod = "client_steamid"; $sql = "SELECT * FROM `EntWatch_Current_Eban` WHERE `$searchMethod`=?"; $stmt = $GLOBALS['DB']->prepare($sql); $stmt->bind_param("s", $search); $stmt->execute(); $queryA = $stmt->get_result(); $rows = $queryA->num_rows; $queryA->free(); return $rows; } public function GetRealEbansNumber($steamID) { $search = $steamID; $searchMethod = "client_steamid"; // "Real" ebans = still active, auto-expired, or expired-and-archived - // excludes bans an admin manually removed for a non-expiry reason // (e.g. pardons), same distinction the old two-table version made. $sql = "SELECT * FROM `EntWatch_Current_Eban` WHERE `$searchMethod`=? AND (`admin_steamid_unban` IS NULL OR `admin_steamid_unban` = '' OR `admin_steamid_unban` = 'SERVER' OR `reason_unban` = 'Expired')"; $stmt = $GLOBALS['DB']->prepare($sql); $stmt->bind_param("s", $search); $stmt->execute(); $queryA = $stmt->get_result(); $rows = $queryA->num_rows; $queryA->free(); return $rows; } public function addNewEban($playerNameA, $playerSteamID, $length, $reasonA) { $admin = new Admin(); $admin->UpdateAdminInfo($_COOKIE['steamID']); $adminName = $admin->adminUser; $adminSteamID = $admin->adminSteamID; $adminID = $admin->adminID; $playerName = Utility::sanitizeInput($playerNameA); $reason = Utility::sanitizeInput($reasonA); $lengthInMinutes = ($length / 60); if ($length <= -1) { $lengthInMinutes = 30; } elseif ($length == 0) { $lengthInMinutes = 0; } if ($this->IsSteamIDAlreadyBanned($playerSteamID)) { die(); } if ($lengthInMinutes == 0) { // Permanent - no playtime baseline needed $time_played_start = -1; $time_played_end = -1; } else { $time_played_start = $this->GetPlaytimeMinutes($playerSteamID); $time_played_end = ($time_played_start + $lengthInMinutes); } // Prepare and execute INSERT INTO EntWatch_Current_Eban // time_stamp_start uses the column's own DEFAULT current_timestamp(). // server is hardcoded here since the column no longer exists on this table. $sql = "INSERT INTO `EntWatch_Current_Eban` (`client_name`, `client_steamid`, `admin_name`, `admin_steamid`, `reason`, `duration`, `time_played_start`, `time_played_end`, `is_expired`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0)"; if ($stmt = $GLOBALS['DB']->prepare($sql)) { $stmt->bind_param("sssssiii", $playerName, $playerSteamID, $adminName, $adminSteamID, $reason, $lengthInMinutes, $time_played_start, $time_played_end); if (!$stmt->execute()) { error_log("Database error: " . $stmt->error); die("Database error occurred."); } $stmt->close(); } else { error_log("Database prepare error: " . $GLOBALS['DB']->error); die("Database prepare error occurred."); } // Push the same Issued/Expire/Length values into the sourcemod // plugin's own cookie cache, so the in-game restriction matches // what was just written to EntWatch_Current_Eban. $this->SyncRestrictCookies($playerSteamID, $lengthInMinutes, $time_played_start, $time_played_end); echo ""; } public function EditEban($id, $playerNameA, $playerSteamID, $length, $reasonA) { $admin = new Admin(); $admin->UpdateAdminInfo($_COOKIE['steamID']); $adminName = $admin->adminUser; $adminSteamID = $admin->adminSteamID; // Escape single quotes by removing them $playerName = Utility::sanitizeInput($playerNameA); $reason = Utility::sanitizeInput($reasonA); $lengthInMinutes = ($length / 60); $info = $this->getEbanInfoFromID($id); if ($length <= -1) { $lengthInMinutes = 30; } elseif ($length == 0) { $lengthInMinutes = 0; } $time_played_start = intval($info['time_played_start']); if ($lengthInMinutes <= 0) { // Permanent (or zero-length) - no baseline, no expiry threshold $time_played_start = -1; $time_played_end = -1; } else if ($time_played_start == -1) { // Converting from permanent/unbaselined to temporary - there's no // existing baseline to build on, so establish a fresh one now, // the same way a brand new eban does. $time_played_start = $this->GetPlaytimeMinutes($playerSteamID); $time_played_end = ($time_played_start + $lengthInMinutes); } else { $time_played_end = ($time_played_start + $lengthInMinutes); } // time_played_start is normally left untouched on an edit - it's the // plugin's own baseline. The one exception (handled above) is // converting a permanent/unbaselined ban into a temporary one, where // there was never a real baseline to preserve in the first place. $sql = "UPDATE `EntWatch_Current_Eban` SET `client_name` = ?, `client_steamid` = ?, `reason` = ?, `duration` = ?, `time_played_start` = ?, `time_played_end` = ?, `is_expired` = 0 WHERE `id` = ?"; $stmt = $GLOBALS['DB']->prepare($sql); $stmt->bind_param("sssiiii", $playerName, $playerSteamID, $reason, $lengthInMinutes, $time_played_start, $time_played_end, $id); $stmt->execute(); $stmt->close(); // Push the updated duration/expiry into the plugin's cookie cache // too, otherwise the new duration set here has no effect in-game. $this->SyncRestrictCookies($playerSteamID, $lengthInMinutes, $time_played_start, $time_played_end); echo ""; //echo ""; } public function IsSteamIDAlreadyBanned($steamID) { $sql = "SELECT * FROM `EntWatch_Current_Eban` WHERE `client_steamid`=?"; $stmt = $GLOBALS['DB']->prepare($sql); $stmt->bind_param("s", $steamID); $stmt->execute(); $query = $stmt->get_result(); $results = $query->fetch_all(MYSQLI_ASSOC); $query->free(); foreach ($results as $result) { $isActive = ($result['is_expired'] == 0); if ($isActive) { return true; // Early return when a matching active ban is found } } return false; } } function IsAdminLoggedIn() { if (!isset($_COOKIE['steamID']) || !isset($_COOKIE['secret_key'])) { return false; } $steamID = $_COOKIE['steamID']; $secret_key = $_COOKIE['secret_key']; $admin = new Admin(); if ($admin->IsLoginValid($steamID, $secret_key, false)) { return true; } return false; } function formatMethod(int $method) { $methods = ["", "client_steamid", "client_name", "", "admin_name", "admin_steamid"]; return $methods[$method]; } function GetRowInfo($id, $result2 = null) { $admin = new Admin(); $Eban = new Eban(); if ($id != 0) { $result2 = $Eban->getEbanInfoFromID($id); } else { $id = $result2['id']; } $clientName = $result2['client_name']; $clientSteamID = $result2['client_steamid']; $adminSteamID = $result2['admin_steamid']; $reason = $result2['reason']; $time_stamp_start = strtotime($result2['time_stamp_start']); $duration = $result2['duration']; $time_played_start = intval($result2['time_played_start']); $isExpired = ($result2['is_expired'] == 1) ? true : false; $timestamp_unban = $result2['timestamp_unban']; $adminNameRemoved = $result2['admin_name_unban']; $adminSteamIDRemoved = $result2['admin_steamid_unban']; $timestamp_unban = $result2['timestamp_unban']; $reason_unban = $result2['reason_unban']; $adminName = $admin->GetAdminNameFromSteamID($adminSteamID); $isRemoved = ($adminSteamIDRemoved != "" && $adminSteamIDRemoved != "SERVER") ? true : false; $isPermanent = ($duration == 0); if ($isPermanent) { $length = "Permanent"; $startedOn = "N/A"; $expiresOn = "Never"; } else { $length = $Eban->formatPlaytime($duration); $startedOn = $Eban->formatPlaytime($time_played_start) . " played"; $expiresOn = $Eban->formatPlaytime($time_played_start + $duration) . " played"; } $status = "Eban Active"; if ($isExpired && !$isRemoved) { $status = "Eban Expired"; } if ($isRemoved) { $status = "Eban Removed"; } echo "
"; $href = "ViewPlayerHistory(\"$clientSteamID\", 1);"; echo ""; if (IsAdminLoggedIn()) { $admin->UpdateAdminInfo($_COOKIE['steamID']); if ($isRemoved == false && $isExpired == false) { if ($admin->DoesHaveFullAccess() || $adminSteamID == $admin->adminSteamID) { $editFunction = "EditFromID(\"$id\")"; echo ""; $unbanFunction = "ConfirmUnban($id, \"$clientName\", \"$clientSteamID\");"; echo ""; } } else { if (!$Eban->IsSteamIDAlreadyBanned($clientSteamID)) { $reBanFunction = "RebanFromID(\"$id\");"; echo ""; } } } if ($admin->DoesHaveFullAccess()) { $deleteFunction = "RemoveEbanFromDBCheck($id);"; echo ""; } if (!IsAdminLoggedIn()) { $href = "Login();"; echo ""; } echo "
"; $date = new DateTime("now", new DateTimeZone(DATE_TIME_ZONE)); $date->setTimestamp($time_stamp_start); $startDate = $date->format(DATE_TIME_FORMAT); echo ""; } function GetEbanLengths() { echo ""; } function GetEbanLengthTypes() { echo ""; } ?>