Ban rewrite
This commit is contained in:
parent
5af1f8f76a
commit
56de9f6684
9 changed files with 679 additions and 860 deletions
|
|
@ -1267,328 +1267,271 @@ namespace TShockAPI
|
|||
|
||||
private static void Ban(CommandArgs args)
|
||||
{
|
||||
//Ban syntax:
|
||||
// ban add <target> [reason] [duration] [flags (default: -a -u -ip)]
|
||||
// Valid flags: -a (ban account name), -u (ban UUID), -n (ban character name), -ip (ban IP address), -e (exact, ban the identifier provided as 'target')
|
||||
// Unless -e is passed to the command, <target> is assumed to be a player or player index.
|
||||
// ban del <target>
|
||||
// Target is expected to be an identifier in the format 'identifier_prefix:identifier'. Eg acc:MyAccountName
|
||||
// ban list [page]
|
||||
// Displays a paginated list of bans
|
||||
// ban details <target>
|
||||
// Target is expected to be an identifier in the format 'identifier_prefix:identifier'. Eg acc:MyAccountName
|
||||
// Output: Banned Identifier - expiration
|
||||
// Reason: text
|
||||
// Banned by: name
|
||||
|
||||
void Help()
|
||||
{
|
||||
if (args.Parameters.Count > 1)
|
||||
{
|
||||
MoreHelp(args.Parameters[1].ToLower());
|
||||
return;
|
||||
}
|
||||
|
||||
args.Player.SendMessage("TShock Ban Help", Color.White);
|
||||
args.Player.SendMessage("Available Ban commands:", Color.White);
|
||||
args.Player.SendMessage("ban [c/FFAAAA:add] <target> [flags]", Color.White);
|
||||
args.Player.SendMessage("ban [c/FFAAAA:del] <target>", Color.White);
|
||||
args.Player.SendMessage("ban [c/FFAAAA:list]", Color.White);
|
||||
args.Player.SendMessage("ban [c/FFAAAA:details] <target>", Color.White);
|
||||
args.Player.SendMessage("For more info, use [c/AAAAFF:ban help] [c/FFAAAA:command]", Color.White);
|
||||
}
|
||||
|
||||
void MoreHelp(string cmd)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case "add":
|
||||
args.Player.SendMessage("", Color.White);
|
||||
args.Player.SendMessage("Ban Add Syntax", Color.White);
|
||||
args.Player.SendMessage("[c/AAAAFF:ban add] [c/FFAAAA:<target>] [[c/AAAAFF:reason]] [[c/FFAAFF:duration]] [[c/AAFFAA:flags]]", Color.White);
|
||||
args.Player.SendMessage("- [c/FFAAFF:Duration]: uses the format [c/FFAAFF:0d0m0s] to determine the length of the ban. Eg a value of [c/FFAAFF:10d30m0s] would represent 10 days, 30 minutes, 0 seconds.", Color.White);
|
||||
args.Player.SendMessage("- [c/AAFFAA:flags]: -a (account name), -u (UUID), -n (character name), -ip (IP address), -e (exact, [c/FFAAAA:target] will be treated as identifier)", Color.White);
|
||||
args.Player.SendMessage(" Unless [c/AAFFAA:-e] is passed to the command, [c/FFAAAA:target] is assumed to be a player or player index", Color.White);
|
||||
args.Player.SendMessage(" If no [c/AAFFAA:flags] are specified, the command uses [c/AAFFAA:-a -u -ip] by default.", Color.White);
|
||||
args.Player.SendMessage("Example usage: [c/AAAAFF:ban add] [c/FFAAAA:ExamplePlayer] [c/AAAAFF:\"Cheating\"] 10d30m0s [c/AAFFAA:-a -u -ip]", Color.White);
|
||||
break;
|
||||
|
||||
case "del":
|
||||
args.Player.SendMessage("", Color.White);
|
||||
args.Player.SendMessage("Ban Del Syntax", Color.White);
|
||||
args.Player.SendMessage("[c/AAAAFF:ban del] [c/FFAAAA:target]", Color.White);
|
||||
args.Player.SendMessage("- [c/FFAAAA:Target] is expected to be an identifier in the format 'identifier_prefix:identifier'. Eg [c/FFAAAA:acc:MyAccountName]", Color.White);
|
||||
args.Player.SendMessage("Example usage: [c/AAAAFF:ban del] [c/FFAAAA:acc:ExampleAccount]", Color.White);
|
||||
break;
|
||||
|
||||
case "list":
|
||||
args.Player.SendMessage("", Color.White);
|
||||
args.Player.SendMessage("Ban List Syntax", Color.White);
|
||||
args.Player.SendMessage("[c/AAAAFF:ban list] [[c/FFAAFF:page]]", Color.White);
|
||||
args.Player.SendMessage("- Lists active bans. Color trends towards green as the ban approaches expiration", Color.White);
|
||||
args.Player.SendMessage("Example usage: [c/AAAAFF:ban list]", Color.White);
|
||||
break;
|
||||
|
||||
case "details":
|
||||
args.Player.SendMessage("", Color.White);
|
||||
args.Player.SendMessage("Ban Details Syntax", Color.White);
|
||||
args.Player.SendMessage("[c/AAAAFF:ban details] [c/FFAAAA:target]", Color.White);
|
||||
args.Player.SendMessage("- [c/FFAAAA:Target] is expected to be an identifier in the format 'identifier_prefix:identifier'. Eg [c/FFAAAA:acc:MyAccountName]", Color.White);
|
||||
args.Player.SendMessage("Example usage: [c/AAAAFF:ban details] [c/FFAAAA:acc:ExampleAccount]", Color.White);
|
||||
break;
|
||||
|
||||
default:
|
||||
args.Player.SendMessage("Unknown ban command. Try 'add', 'del', 'list', or 'details'", Color.White);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AddBan()
|
||||
{
|
||||
if (!args.Parameters.TryGetValue(1, out string target))
|
||||
{
|
||||
args.Player.SendMessage("Invalid Ban Add syntax. Refer to [c/AAAAFF:ban help add] for details on how to use the [c/AAAAFF:ban add] command", Color.White);
|
||||
return;
|
||||
}
|
||||
|
||||
bool exactTarget = args.Parameters.Any(p => p == "-e");
|
||||
bool banAccount = args.Parameters.Any(p => p == "-a");
|
||||
bool banUuid = args.Parameters.Any(p => p == "-u");
|
||||
bool banName = args.Parameters.Any(p => p == "-n");
|
||||
bool banIp = args.Parameters.Any(p => p == "-ip");
|
||||
|
||||
args.Parameters.TryGetValue(2, out string reason);
|
||||
args.Parameters.TryGetValue(3, out string duration);
|
||||
DateTime expiration = DateTime.MaxValue;
|
||||
|
||||
if (TShock.Utils.TryParseTime(duration, out int seconds))
|
||||
{
|
||||
expiration = DateTime.UtcNow.AddSeconds(seconds);
|
||||
}
|
||||
|
||||
//If no flags were specified, default to account, uuid, and IP
|
||||
if (!exactTarget && !banAccount && !banUuid && !banName && !banIp)
|
||||
{
|
||||
banAccount = banUuid = banIp = true;
|
||||
}
|
||||
|
||||
if (exactTarget)
|
||||
{
|
||||
if (TShock.Bans.InsertBan(target, reason ?? "Banned", args.Player.Account.Name, DateTime.UtcNow, expiration) != null)
|
||||
{
|
||||
args.Player.SendSuccessMessage("Ban added.");
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Player.SendErrorMessage("Failed to insert ban. Ban may already exist, or an error occured.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var players = TSPlayer.FindByNameOrID(target);
|
||||
|
||||
if (players.Count > 1)
|
||||
{
|
||||
args.Player.SendMultipleMatchError(players.Select(p => p.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
if (players.Count < 1)
|
||||
{
|
||||
args.Player.SendErrorMessage("Could not find the target specified. Check that you have the correct spelling.");
|
||||
return;
|
||||
}
|
||||
|
||||
var player = players[0];
|
||||
var identifiers = new List<string>();
|
||||
string identifier;
|
||||
|
||||
if (banAccount)
|
||||
{
|
||||
if (player.Account != null)
|
||||
{
|
||||
identifier = $"{DB.Ban.Identifiers.Account}{player.Account.Name}";
|
||||
if (TShock.Bans.InsertBan(identifier, reason, args.Player.Account.Name, DateTime.UtcNow, expiration) != null)
|
||||
{
|
||||
identifiers.Add(identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (banUuid)
|
||||
{
|
||||
identifier = $"{DB.Ban.Identifiers.UUID}{player.UUID}";
|
||||
if (TShock.Bans.InsertBan($"{DB.Ban.Identifiers.UUID}{player.UUID}", reason, args.Player.Account.Name, DateTime.UtcNow, expiration) != null)
|
||||
{
|
||||
identifiers.Add(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
if (banName)
|
||||
{
|
||||
identifier = $"{DB.Ban.Identifiers.Name}{player.Name}";
|
||||
if (TShock.Bans.InsertBan($"{DB.Ban.Identifiers.Name}{player.Name}", reason, args.Player.Account.Name, DateTime.UtcNow, expiration) != null)
|
||||
{
|
||||
identifiers.Add(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
if (banIp)
|
||||
{
|
||||
identifier = $"{DB.Ban.Identifiers.IP}{player.IP}";
|
||||
if (TShock.Bans.InsertBan($"{DB.Ban.Identifiers.IP}{player.IP}", reason, args.Player.Account.Name, DateTime.UtcNow, expiration) != null)
|
||||
{
|
||||
identifiers.Add(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
args.Player.SendSuccessMessage("Bans added for identifiers: ", string.Join(", ", identifiers));
|
||||
}
|
||||
|
||||
void DelBan()
|
||||
{
|
||||
if (!args.Parameters.TryGetValue(1, out string target))
|
||||
{
|
||||
args.Player.SendMessage("Invalid Ban Del syntax. Refer to [c/AAAAFF:ban help del] for details on how to use the [c/AAAAFF:ban del] command", Color.White);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TShock.Bans.RemoveBan(target))
|
||||
{
|
||||
args.Player.SendSuccessMessage("Ban removed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Player.SendErrorMessage("Failed to remove ban.");
|
||||
}
|
||||
}
|
||||
|
||||
void ListBans()
|
||||
{
|
||||
int pageNumber;
|
||||
if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
|
||||
{
|
||||
args.Player.SendMessage("Invalid Ban List syntax. Refer to [c/AAAAFF:ban help list] for details on how to use the [c/AAAAFF:ban list] command", Color.White);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Ban> bans = TShock.Bans.GetAllBans();
|
||||
|
||||
var nameBans = from ban in bans
|
||||
select ban.Identifier;
|
||||
|
||||
PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(nameBans),
|
||||
new PaginationTools.Settings
|
||||
{
|
||||
HeaderFormat = "Bans ({0}/{1}):",
|
||||
FooterFormat = "Type {0}ban list {{0}} for more.".SFormat(Specifier),
|
||||
NothingToDisplayString = "There are currently no bans."
|
||||
});
|
||||
}
|
||||
|
||||
void BanDetails()
|
||||
{
|
||||
if (!args.Parameters.TryGetValue(1, out string target))
|
||||
{
|
||||
args.Player.SendMessage("Invalid Ban Details syntax. Refer to [c/AAAAFF:ban help details] for details on how to use the [c/AAAAFF:ban details] command", Color.White);
|
||||
return;
|
||||
}
|
||||
|
||||
Ban ban = TShock.Bans.GetBanByIdentifier(target);
|
||||
|
||||
if (ban == null)
|
||||
{
|
||||
args.Player.SendErrorMessage("No ban found matching the given identifier");
|
||||
return;
|
||||
}
|
||||
|
||||
args.Player.SendMessage($"{ban.Identifier}", Color.White);
|
||||
args.Player.SendMessage($"Reason: {ban.Reason}", Color.White);
|
||||
args.Player.SendMessage($"Banned by: [c/AAFFAA:{ban.BanningUser}] at [c/AAAAFF:time]", Color.White);
|
||||
}
|
||||
|
||||
string subcmd = args.Parameters.Count == 0 ? "help" : args.Parameters[0].ToLower();
|
||||
switch (subcmd)
|
||||
{
|
||||
case "add":
|
||||
#region Add Ban
|
||||
{
|
||||
if (args.Parameters.Count < 2)
|
||||
{
|
||||
args.Player.SendErrorMessage("Invalid command. Format: {0}ban add <player> [time] [reason]", Specifier);
|
||||
args.Player.SendErrorMessage("Example: {0}ban add Shank 10d Hacking and cheating", Specifier);
|
||||
args.Player.SendErrorMessage("Example: {0}ban add Ash", Specifier);
|
||||
args.Player.SendErrorMessage("Use the time 0 (zero) for a permanent ban.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Used only to notify if a ban was successful and who the ban was about
|
||||
bool success = false;
|
||||
string targetGeneralizedName = "";
|
||||
|
||||
// Effective ban target assignment
|
||||
List<TSPlayer> players = TSPlayer.FindByNameOrID(args.Parameters[1]);
|
||||
|
||||
// Bad case: Players contains more than 1 person so we can't ban them
|
||||
if (players.Count > 1)
|
||||
{
|
||||
//Fail fast
|
||||
args.Player.SendMultipleMatchError(players.Select(p => p.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
UserAccount offlineUserAccount = TShock.UserAccounts.GetUserAccountByName(args.Parameters[1]);
|
||||
|
||||
// Storage variable to determine if the command executor is the server console
|
||||
// If it is, we assume they have full control and let them override permission checks
|
||||
bool callerIsServerConsole = args.Player is TSServerPlayer;
|
||||
|
||||
// The ban reason the ban is going to have
|
||||
string banReason = "Unknown.";
|
||||
|
||||
// The default ban length
|
||||
// 0 is permanent ban, otherwise temp ban
|
||||
int banLengthInSeconds = 0;
|
||||
|
||||
// Figure out if param 2 is a time or 0 or garbage
|
||||
if (args.Parameters.Count >= 3)
|
||||
{
|
||||
bool parsedOkay = false;
|
||||
if (args.Parameters[2] != "0")
|
||||
{
|
||||
parsedOkay = TShock.Utils.TryParseTime(args.Parameters[2], out banLengthInSeconds);
|
||||
}
|
||||
else
|
||||
{
|
||||
parsedOkay = true;
|
||||
}
|
||||
|
||||
if (!parsedOkay)
|
||||
{
|
||||
args.Player.SendErrorMessage("Invalid time format. Example: 10d 5h 3m 2s.");
|
||||
args.Player.SendErrorMessage("Use 0 (zero) for a permanent ban.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If a reason exists, use the given reason.
|
||||
if (args.Parameters.Count > 3)
|
||||
{
|
||||
banReason = String.Join(" ", args.Parameters.Skip(3));
|
||||
}
|
||||
|
||||
// Good case: Online ban for matching character.
|
||||
if (players.Count == 1)
|
||||
{
|
||||
TSPlayer target = players[0];
|
||||
|
||||
if (target.HasPermission(Permissions.immunetoban) && !callerIsServerConsole)
|
||||
{
|
||||
args.Player.SendErrorMessage("Permission denied. Target {0} is immune to ban.", target.Name);
|
||||
return;
|
||||
}
|
||||
|
||||
targetGeneralizedName = target.Name;
|
||||
success = TShock.Bans.AddBan(target.IP, target.Name, target.UUID, target.Account?.Name ?? "", banReason, false, args.Player.Account.Name,
|
||||
banLengthInSeconds == 0 ? "" : DateTime.UtcNow.AddSeconds(banLengthInSeconds).ToString("s"));
|
||||
|
||||
// Since this is an online ban, we need to dc the player and tell them now.
|
||||
if (success)
|
||||
{
|
||||
if (banLengthInSeconds == 0)
|
||||
{
|
||||
target.Disconnect(String.Format("Permanently banned for {0}", banReason));
|
||||
}
|
||||
else
|
||||
{
|
||||
target.Disconnect(String.Format("Banned for {0} seconds for {1}", banLengthInSeconds, banReason));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Case: Players & user are invalid, could be IP?
|
||||
// Note: Order matters. If this method is above the online player check,
|
||||
// This enables you to ban an IP even if the player exists in the database as a player.
|
||||
// You'll get two bans for the price of one, in theory, because both IP and user named IP will be banned.
|
||||
// ??? edge cases are weird, but this is going to happen
|
||||
// The only way around this is to either segregate off the IP code or do something else.
|
||||
if (players.Count == 0)
|
||||
{
|
||||
// If the target is a valid IP...
|
||||
string pattern = @"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";
|
||||
Regex r = new Regex(pattern, RegexOptions.IgnoreCase);
|
||||
if (r.IsMatch(args.Parameters[1]))
|
||||
{
|
||||
targetGeneralizedName = "IP: " + args.Parameters[1];
|
||||
success = TShock.Bans.AddBan(args.Parameters[1], "", "", "", banReason,
|
||||
false, args.Player.Account.Name, banLengthInSeconds == 0 ? "" : DateTime.UtcNow.AddSeconds(banLengthInSeconds).ToString("s"));
|
||||
if (success && offlineUserAccount != null)
|
||||
{
|
||||
args.Player.SendSuccessMessage("Target IP {0} was banned successfully.", targetGeneralizedName);
|
||||
args.Player.SendErrorMessage("Note: An account named with this IP address also exists.");
|
||||
args.Player.SendErrorMessage("Note: It will also be banned.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Apparently there is no way to not IP ban someone
|
||||
// This means that where we would normally just ban a "character name" here
|
||||
// We can't because it requires some IP as a primary key.
|
||||
if (offlineUserAccount == null)
|
||||
{
|
||||
args.Player.SendErrorMessage("Unable to ban target {0}.", args.Parameters[1]);
|
||||
args.Player.SendErrorMessage("Target is not a valid IP address, a valid online player, or a known offline user.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Case: Offline ban
|
||||
if (players.Count == 0 && offlineUserAccount != null)
|
||||
{
|
||||
// Catch: we don't know an offline player's last login character name
|
||||
// This means that we're banning their *user name* on the assumption that
|
||||
// user name == character name
|
||||
// (which may not be true)
|
||||
// This needs to be fixed in a future implementation.
|
||||
targetGeneralizedName = offlineUserAccount.Name;
|
||||
|
||||
if (TShock.Groups.GetGroupByName(offlineUserAccount.Group).HasPermission(Permissions.immunetoban) &&
|
||||
!callerIsServerConsole)
|
||||
{
|
||||
args.Player.SendErrorMessage("Permission denied. Target {0} is immune to ban.", targetGeneralizedName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (offlineUserAccount.KnownIps == null)
|
||||
{
|
||||
args.Player.SendErrorMessage("Unable to ban target {0} because they have no valid IP to ban.", targetGeneralizedName);
|
||||
return;
|
||||
}
|
||||
|
||||
string lastIP = JsonConvert.DeserializeObject<List<string>>(offlineUserAccount.KnownIps).Last();
|
||||
|
||||
success =
|
||||
TShock.Bans.AddBan(lastIP,
|
||||
"", offlineUserAccount.UUID, offlineUserAccount.Name, banReason, false, args.Player.Account.Name,
|
||||
banLengthInSeconds == 0 ? "" : DateTime.UtcNow.AddSeconds(banLengthInSeconds).ToString("s"));
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
args.Player.SendSuccessMessage("{0} was successfully banned.", targetGeneralizedName);
|
||||
args.Player.SendInfoMessage("Length: {0}", banLengthInSeconds == 0 ? "Permanent." : banLengthInSeconds + " seconds.");
|
||||
args.Player.SendInfoMessage("Reason: {0}", banReason);
|
||||
if (!args.Silent)
|
||||
{
|
||||
if (banLengthInSeconds == 0)
|
||||
{
|
||||
TSPlayer.All.SendErrorMessage("{0} was permanently banned by {1} for: {2}",
|
||||
targetGeneralizedName, args.Player.Account.Name, banReason);
|
||||
}
|
||||
else
|
||||
{
|
||||
TSPlayer.All.SendErrorMessage("{0} was temp banned for {1} seconds by {2} for: {3}",
|
||||
targetGeneralizedName, banLengthInSeconds, args.Player.Account.Name, banReason);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Player.SendErrorMessage("{0} was NOT banned due to a database error or other system problem.", targetGeneralizedName);
|
||||
args.Player.SendErrorMessage("If this player is online, they have NOT been kicked.");
|
||||
args.Player.SendErrorMessage("Check the system logs for details.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
case "del":
|
||||
#region Delete ban
|
||||
{
|
||||
if (args.Parameters.Count != 2)
|
||||
{
|
||||
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}ban del <player>", Specifier);
|
||||
return;
|
||||
}
|
||||
|
||||
string plStr = args.Parameters[1];
|
||||
Ban ban = TShock.Bans.GetBanByName(plStr, false);
|
||||
if (ban != null)
|
||||
{
|
||||
if (TShock.Bans.RemoveBan(ban.Name, true))
|
||||
args.Player.SendSuccessMessage("Unbanned {0} ({1}).", ban.Name, ban.IP);
|
||||
else
|
||||
args.Player.SendErrorMessage("Failed to unban {0} ({1}), check logs.", ban.Name, ban.IP);
|
||||
}
|
||||
else
|
||||
args.Player.SendErrorMessage("No bans for {0} exist.", plStr);
|
||||
}
|
||||
#endregion
|
||||
return;
|
||||
case "delip":
|
||||
#region Delete IP ban
|
||||
{
|
||||
if (args.Parameters.Count != 2)
|
||||
{
|
||||
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}ban delip <ip>", Specifier);
|
||||
return;
|
||||
}
|
||||
|
||||
string ip = args.Parameters[1];
|
||||
Ban ban = TShock.Bans.GetBanByIp(ip);
|
||||
if (ban != null)
|
||||
{
|
||||
if (TShock.Bans.RemoveBan(ban.IP, false))
|
||||
args.Player.SendSuccessMessage("Unbanned IP {0} ({1}).", ban.IP, ban.Name);
|
||||
else
|
||||
args.Player.SendErrorMessage("Failed to unban IP {0} ({1}), check logs.", ban.IP, ban.Name);
|
||||
}
|
||||
else
|
||||
args.Player.SendErrorMessage("IP {0} is not banned.", ip);
|
||||
}
|
||||
#endregion
|
||||
return;
|
||||
case "help":
|
||||
#region Help
|
||||
{
|
||||
int pageNumber;
|
||||
if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
|
||||
return;
|
||||
Help();
|
||||
break;
|
||||
|
||||
var lines = new List<string>
|
||||
{
|
||||
"add <target> <time> [reason] - Bans a player or user account if the player is not online.",
|
||||
"del <player> - Unbans a player.",
|
||||
"delip <ip> - Unbans an IP.",
|
||||
"list [page] - Lists all player bans.",
|
||||
"listip [page] - Lists all IP bans."
|
||||
};
|
||||
case "add":
|
||||
AddBan();
|
||||
break;
|
||||
|
||||
case "del":
|
||||
DelBan();
|
||||
break;
|
||||
|
||||
PaginationTools.SendPage(args.Player, pageNumber, lines,
|
||||
new PaginationTools.Settings
|
||||
{
|
||||
HeaderFormat = "Ban Sub-Commands ({0}/{1}):",
|
||||
FooterFormat = "Type {0}ban help {{0}} for more sub-commands.".SFormat(Specifier)
|
||||
}
|
||||
);
|
||||
}
|
||||
#endregion
|
||||
return;
|
||||
case "list":
|
||||
#region List bans
|
||||
{
|
||||
int pageNumber;
|
||||
if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
|
||||
{
|
||||
return;
|
||||
}
|
||||
ListBans();
|
||||
break;
|
||||
|
||||
List<Ban> bans = TShock.Bans.GetBans();
|
||||
case "details":
|
||||
BanDetails();
|
||||
break;
|
||||
|
||||
var nameBans = from ban in bans
|
||||
where !String.IsNullOrEmpty(ban.Name)
|
||||
select ban.Name;
|
||||
|
||||
PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(nameBans),
|
||||
new PaginationTools.Settings
|
||||
{
|
||||
HeaderFormat = "Bans ({0}/{1}):",
|
||||
FooterFormat = "Type {0}ban list {{0}} for more.".SFormat(Specifier),
|
||||
NothingToDisplayString = "There are currently no bans."
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
return;
|
||||
case "listip":
|
||||
#region List IP bans
|
||||
{
|
||||
int pageNumber;
|
||||
if (!PaginationTools.TryParsePageNumber(args.Parameters, 1, args.Player, out pageNumber))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<Ban> bans = TShock.Bans.GetBans();
|
||||
|
||||
var ipBans = from ban in bans
|
||||
where String.IsNullOrEmpty(ban.Name)
|
||||
select ban.IP;
|
||||
|
||||
PaginationTools.SendPage(args.Player, pageNumber, PaginationTools.BuildLinesFromTerms(ipBans),
|
||||
new PaginationTools.Settings
|
||||
{
|
||||
HeaderFormat = "IP Bans ({0}/{1}):",
|
||||
FooterFormat = "Type {0}ban listip {{0}} for more.".SFormat(Specifier),
|
||||
NothingToDisplayString = "There are currently no IP bans."
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
return;
|
||||
default:
|
||||
args.Player.SendErrorMessage("Invalid subcommand! Type {0}ban help for more information.", Specifier);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,15 @@ namespace TShockAPI.DB
|
|||
{
|
||||
private IDbConnection database;
|
||||
|
||||
/// <summary>
|
||||
/// Event invoked when a ban check occurs
|
||||
/// </summary>
|
||||
public static event EventHandler<BanEventArgs> OnBanCheck;
|
||||
/// <summary>
|
||||
/// Event invoked when a ban is added
|
||||
/// </summary>
|
||||
public static event EventHandler<BanEventArgs> OnBanAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TShockAPI.DB.BanManager"/> class.
|
||||
/// </summary>
|
||||
|
|
@ -39,15 +48,12 @@ namespace TShockAPI.DB
|
|||
{
|
||||
database = db;
|
||||
|
||||
var table = new SqlTable("Bans",
|
||||
new SqlColumn("IP", MySqlDbType.String, 16) { Primary = true },
|
||||
new SqlColumn("Name", MySqlDbType.Text),
|
||||
new SqlColumn("UUID", MySqlDbType.Text),
|
||||
var table = new SqlTable("PlayerBans",
|
||||
new SqlColumn("Identifier", MySqlDbType.Text) { Primary = true, Unique = true },
|
||||
new SqlColumn("Reason", MySqlDbType.Text),
|
||||
new SqlColumn("BanningUser", MySqlDbType.Text),
|
||||
new SqlColumn("Date", MySqlDbType.Text),
|
||||
new SqlColumn("Expiration", MySqlDbType.Text),
|
||||
new SqlColumn("AccountName", MySqlDbType.Text)
|
||||
new SqlColumn("Expiration", MySqlDbType.Text)
|
||||
);
|
||||
var creator = new SqlTableCreator(db,
|
||||
db.GetSqlType() == SqlType.Sqlite
|
||||
|
|
@ -62,55 +68,204 @@ namespace TShockAPI.DB
|
|||
System.Console.WriteLine("Possible problem with your database - is Sqlite3.dll present?");
|
||||
throw new Exception("Could not find a database library (probably Sqlite3.dll)");
|
||||
}
|
||||
|
||||
OnBanCheck += CheckBanValid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a ban by IP.
|
||||
/// Converts bans from the old ban system to the new.
|
||||
/// </summary>
|
||||
/// <param name="ip">The IP.</param>
|
||||
/// <returns>The ban.</returns>
|
||||
public Ban GetBanByIp(string ip)
|
||||
public void ConvertBans()
|
||||
{
|
||||
try
|
||||
using (var reader = database.QueryReader("SELECT * FROM Bans"))
|
||||
{
|
||||
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE IP=@0", ip))
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.Read())
|
||||
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("AccountName"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
|
||||
var ip = reader.Get<string>("IP");
|
||||
var uuid = reader.Get<string>("UUID");
|
||||
var account = reader.Get<string>("AccountName");
|
||||
var reason = reader.Get<string>("Reason");
|
||||
var banningUser = reader.Get<string>("BanningUser");
|
||||
var date = reader.Get<string>("Date");
|
||||
var expiration = reader.Get<string>("Expiration");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ip))
|
||||
{
|
||||
InsertBan($"{Ban.Identifiers.IP}{ip}", reason, banningUser, date, expiration);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(account))
|
||||
{
|
||||
InsertBan($"{Ban.Identifiers.Account}{account}", reason, banningUser, date, expiration);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(uuid))
|
||||
{
|
||||
InsertBan($"{Ban.Identifiers.UUID}{uuid}", reason, banningUser, date, expiration);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not a ban is valid
|
||||
/// </summary>
|
||||
/// <param name="ban"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsValidBan(Ban ban)
|
||||
{
|
||||
BanEventArgs args = new BanEventArgs { Ban = ban };
|
||||
OnBanCheck?.Invoke(this, args);
|
||||
|
||||
return args.Valid;
|
||||
}
|
||||
|
||||
internal void CheckBanValid(object sender, BanEventArgs args)
|
||||
{
|
||||
//We consider a ban to be valid if the start time is before now and the end time is after now
|
||||
args.Valid = args.Ban.BanDateTime < DateTime.UtcNow && args.Ban.ExpirationDateTime > DateTime.UtcNow;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new ban for the given identifier. If the addition succeeds, returns a ban object with the ban details. Else returns null
|
||||
/// </summary>
|
||||
/// <param name="identifier"></param>
|
||||
/// <param name="reason"></param>
|
||||
/// <param name="banningUser"></param>
|
||||
/// <param name="fromDate"></param>
|
||||
/// <param name="toDate"></param>
|
||||
/// <returns></returns>
|
||||
public Ban InsertBan(string identifier, string reason, string banningUser, DateTime fromDate, DateTime toDate)
|
||||
=> InsertBan(identifier, reason, banningUser, fromDate.ToString("s"), toDate.ToString("s"));
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new ban for the given identifier. If the addition succeeds, returns a ban object with the ban details. Else returns null
|
||||
/// </summary>
|
||||
/// <param name="identifier"></param>
|
||||
/// <param name="reason"></param>
|
||||
/// <param name="banningUser"></param>
|
||||
/// <param name="fromDate"></param>
|
||||
/// <param name="toDate"></param>
|
||||
/// <returns></returns>
|
||||
public Ban InsertBan(string identifier, string reason, string banningUser, string fromDate, string toDate)
|
||||
{
|
||||
Ban b = new Ban(identifier, reason, banningUser, fromDate, toDate);
|
||||
|
||||
BanEventArgs args = new BanEventArgs { Ban = b };
|
||||
|
||||
OnBanAdded?.Invoke(this, args);
|
||||
|
||||
if (!args.Valid)
|
||||
{
|
||||
TShock.Log.Error(ex.ToString());
|
||||
return null;
|
||||
}
|
||||
|
||||
int rowsModified = database.Query("INSERT OR IGNORE INTO PlayerBans (Identifier, Reason, BanningUser, Date, Expiration) VALUES (@0, @1, @2, @3, @4);", identifier, reason, banningUser, fromDate, toDate);
|
||||
//Return the ban if the query actually inserted the row. If the given identifier already exists, the INSERT is ignored and 0 rows are modified.
|
||||
return rowsModified != 0 ? b : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to remove a ban. Returns true if the ban was removed or expired. False if the ban could not be removed or expired
|
||||
/// </summary>
|
||||
/// <param name="identifier"></param>
|
||||
/// <param name="fullDelete">If true, deletes the ban from the database. If false, marks the expiration time as now, rendering the ban expired. Defaults to false</param>
|
||||
/// <returns></returns>
|
||||
public bool RemoveBan(string identifier, bool fullDelete = false)
|
||||
{
|
||||
int rowsModified;
|
||||
if (fullDelete)
|
||||
{
|
||||
rowsModified = database.Query("DELETE FROM PlayerBans WHERE Identifier=@0", identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
rowsModified = database.Query("UPDATE PlayerBans SET Expiration=@0 WHERE Identifier=@1", DateTime.UtcNow.ToString("s"), identifier);
|
||||
}
|
||||
|
||||
return rowsModified > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves a ban for a given identifier, or null if no matches are found
|
||||
/// </summary>
|
||||
/// <param name="identifier"></param>
|
||||
/// <returns></returns>
|
||||
public Ban GetBanByIdentifier(string identifier)
|
||||
{
|
||||
using (var reader = database.QueryReader("SELECT * FROM PlayerBans WHERE Identifier=@0", identifier))
|
||||
{
|
||||
if (reader.Read())
|
||||
{
|
||||
var id = reader.Get<string>("Identifier");
|
||||
var reason = reader.Get<string>("Reason");
|
||||
var banningUser = reader.Get<string>("BanningUser");
|
||||
var date = reader.Get<string>("Date");
|
||||
var expiration = reader.Get<string>("Expiration");
|
||||
|
||||
return new Ban(id, reason, banningUser, date, expiration);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an enumerable of bans for a given set of identifiers
|
||||
/// </summary>
|
||||
/// <param name="identifiers"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<Ban> GetBansByIdentifiers(params string[] identifiers)
|
||||
{
|
||||
//Generate a sequence of '@0, @1, @2, ... etc'
|
||||
var parameters = string.Join(", ", Enumerable.Range(0, identifiers.Count()).Select(p => $"@{p}"));
|
||||
|
||||
using (var reader = database.QueryReader($"SELECT * FROM PlayerBans WHERE Identifier IN ({parameters})", identifiers))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
var id = reader.Get<string>("Identifier");
|
||||
var reason = reader.Get<string>("Reason");
|
||||
var banningUser = reader.Get<string>("BanningUser");
|
||||
var date = reader.Get<string>("Date");
|
||||
var expiration = reader.Get<string>("Expiration");
|
||||
|
||||
yield return new Ban(id, reason, banningUser, date, expiration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of bans sorted by their addition date from newest to oldest
|
||||
/// </summary>
|
||||
public List<Ban> GetBans() => GetSortedBans(BanSortMethod.AddedNewestToOldest).ToList();
|
||||
public List<Ban> GetAllBans() => GetAllBansSorted(BanSortMethod.AddedNewestToOldest).ToList();
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an enumerable of <see cref="Ban"/> objects, sorted using the provided sort method
|
||||
/// </summary>
|
||||
/// <param name="sortMethod"></param>
|
||||
/// <returns></returns>
|
||||
public IEnumerable<Ban> GetSortedBans(BanSortMethod sortMethod)
|
||||
public IEnumerable<Ban> GetAllBansSorted(BanSortMethod sortMethod)
|
||||
{
|
||||
List<Ban> banlist = new List<Ban>();
|
||||
try
|
||||
{
|
||||
using (var reader = database.QueryReader("SELECT * FROM Bans"))
|
||||
var orderBy = SortToOrderByMap[sortMethod];
|
||||
using (var reader = database.QueryReader($"SELECT * FROM PlayerBans ORDER BY {orderBy}"))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
banlist.Add(new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("AccountName"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration")));
|
||||
}
|
||||
var identifier = reader.Get<string>("Identifier");
|
||||
var reason = reader.Get<string>("Reason");
|
||||
var banningUser = reader.Get<string>("BanningUser");
|
||||
var date = reader.Get<string>("Date");
|
||||
var expiration = reader.Get<string>("Expiration");
|
||||
|
||||
banlist.Sort(new BanComparer(sortMethod));
|
||||
return banlist;
|
||||
var ban = new Ban(identifier, reason, banningUser, date, expiration);
|
||||
banlist.Add(ban);
|
||||
}
|
||||
}
|
||||
return banlist;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -121,172 +276,14 @@ namespace TShockAPI.DB
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a ban by name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="casesensitive">Whether to check with case sensitivity.</param>
|
||||
/// <returns>The ban.</returns>
|
||||
public Ban GetBanByName(string name, bool casesensitive = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var namecol = casesensitive ? "Name" : "UPPER(Name)";
|
||||
if (!casesensitive)
|
||||
name = name.ToUpper();
|
||||
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE " + namecol + "=@0", name))
|
||||
{
|
||||
if (reader.Read())
|
||||
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("AccountName"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TShock.Log.Error(ex.ToString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a ban by account name (not player/character name).
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="casesensitive">Whether to check with case sensitivity.</param>
|
||||
/// <returns>The ban.</returns>
|
||||
public Ban GetBanByAccountName(string name, bool casesensitive = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
var namecol = casesensitive ? "AccountName" : "UPPER(AccountName)";
|
||||
if (!casesensitive)
|
||||
name = name.ToUpper();
|
||||
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE " + namecol + "=@0", name))
|
||||
{
|
||||
if (reader.Read())
|
||||
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("AccountName"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TShock.Log.Error(ex.ToString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a ban by UUID.
|
||||
/// </summary>
|
||||
/// <param name="uuid">The UUID.</param>
|
||||
/// <returns>The ban.</returns>
|
||||
public Ban GetBanByUUID(string uuid)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var reader = database.QueryReader("SELECT * FROM Bans WHERE UUID=@0", uuid))
|
||||
{
|
||||
if (reader.Read())
|
||||
return new Ban(reader.Get<string>("IP"), reader.Get<string>("Name"), reader.Get<string>("UUID"), reader.Get<string>("AccountName"), reader.Get<string>("Reason"), reader.Get<string>("BanningUser"), reader.Get<string>("Date"), reader.Get<string>("Expiration"));
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TShock.Log.Error(ex.ToString());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a ban.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if ban was added, <c>false</c> otherwise.</returns>
|
||||
/// <param name="ip">Ip.</param>
|
||||
/// <param name="name">Name.</param>
|
||||
/// <param name="uuid">UUID.</param>
|
||||
/// <param name="reason">Reason.</param>
|
||||
/// <param name="exceptions">If set to <c>true</c> enable throwing exceptions.</param>
|
||||
/// <param name="banner">Banner.</param>
|
||||
/// <param name="expiration">Expiration date.</param>
|
||||
public bool AddBan(string ip, string name = "", string uuid = "", string accountName = "", string reason = "", bool exceptions = false, string banner = "", string expiration = "")
|
||||
{
|
||||
try
|
||||
{
|
||||
/*
|
||||
* If the ban already exists, update its entry with the new date
|
||||
* and expiration details.
|
||||
*/
|
||||
if (GetBanByIp(ip) != null)
|
||||
{
|
||||
return database.Query("UPDATE Bans SET Date = @0, Expiration = @1 WHERE IP = @2", DateTime.UtcNow.ToString("s"), expiration, ip) == 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return database.Query("INSERT INTO Bans (IP, Name, UUID, Reason, BanningUser, Date, Expiration, AccountName) VALUES (@0, @1, @2, @3, @4, @5, @6, @7);", ip, name, uuid, reason, banner, DateTime.UtcNow.ToString("s"), expiration, accountName) != 0;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (exceptions)
|
||||
throw ex;
|
||||
TShock.Log.Error(ex.ToString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a ban.
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if ban was removed, <c>false</c> otherwise.</returns>
|
||||
/// <param name="match">Match.</param>
|
||||
/// <param name="byName">If set to <c>true</c> by name.</param>
|
||||
/// <param name="casesensitive">If set to <c>true</c> casesensitive.</param>
|
||||
/// <param name="exceptions">If set to <c>true</c> exceptions.</param>
|
||||
public bool RemoveBan(string match, bool byName = false, bool casesensitive = true, bool exceptions = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!byName)
|
||||
return database.Query("DELETE FROM Bans WHERE IP=@0", match) != 0;
|
||||
|
||||
var namecol = casesensitive ? "Name" : "UPPER(Name)";
|
||||
return database.Query("DELETE FROM Bans WHERE " + namecol + "=@0", casesensitive ? match : match.ToUpper()) != 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (exceptions)
|
||||
throw ex;
|
||||
TShock.Log.Error(ex.ToString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a ban by account name (not character/player name).
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if ban was removed, <c>false</c> otherwise.</returns>
|
||||
/// <param name="match">Match.</param>
|
||||
/// <param name="casesensitive">If set to <c>true</c> casesensitive.</param>
|
||||
public bool RemoveBanByAccountName(string match, bool casesensitive = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var namecol = casesensitive ? "AccountName" : "UPPER(AccountName)";
|
||||
return database.Query("DELETE FROM Bans WHERE " + namecol + "=@0", casesensitive ? match : match.ToUpper()) != 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
TShock.Log.Error(ex.ToString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears bans.
|
||||
/// Removes all bans from the database
|
||||
/// </summary>
|
||||
/// <returns><c>true</c>, if bans were cleared, <c>false</c> otherwise.</returns>
|
||||
public bool ClearBans()
|
||||
{
|
||||
try
|
||||
{
|
||||
return database.Query("DELETE FROM Bans") != 0;
|
||||
return database.Query("DELETE FROM PlayerBans") != 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -295,19 +292,13 @@ namespace TShockAPI.DB
|
|||
return false;
|
||||
}
|
||||
|
||||
/// <summary>Removes a ban if it has expired.</summary>
|
||||
/// <param name="ban">The candidate ban to check.</param>
|
||||
/// <returns>If the ban has been removed.</returns>
|
||||
public bool RemoveBanIfExpired(Ban ban)
|
||||
internal Dictionary<BanSortMethod, string> SortToOrderByMap = new Dictionary<BanSortMethod, string>
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ban.Expiration) && (ban.ExpirationDateTime != null) && (DateTime.UtcNow >= ban.ExpirationDateTime))
|
||||
{
|
||||
RemoveBan(ban.IP, false, false, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
{ BanSortMethod.AddedNewestToOldest, "Date DESC" },
|
||||
{ BanSortMethod.AddedOldestToNewest, "Date ASC" },
|
||||
{ BanSortMethod.ExpirationSoonestToLatest, "Expiration ASC" },
|
||||
{ BanSortMethod.ExpirationLatestToSoonest, "Expiration DESC" }
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -334,88 +325,18 @@ namespace TShockAPI.DB
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// An <see cref="IComparer{Ban}"/> used for sorting an enumerable of bans
|
||||
/// Event args used when a ban check is invoked, or a new ban is added
|
||||
/// </summary>
|
||||
public class BanComparer : IComparer<Ban>
|
||||
public class BanEventArgs : EventArgs
|
||||
{
|
||||
private BanSortMethod _method;
|
||||
|
||||
/// <summary>
|
||||
/// Generates a new <see cref="BanComparer"/> using the given <see cref="BanSortMethod"/>
|
||||
/// The ban being checked or added
|
||||
/// </summary>
|
||||
/// <param name="method"></param>
|
||||
public BanComparer(BanSortMethod method)
|
||||
{
|
||||
_method = method;
|
||||
}
|
||||
|
||||
private int CompareDateTimes(DateTime? x, DateTime? y)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
if (y == null)
|
||||
{
|
||||
//If both bans have no BanDateTime they're considered equal
|
||||
return 0;
|
||||
}
|
||||
//If we're sorting by a newest to oldest method, a null value will come after the valid value.
|
||||
return _method == BanSortMethod.AddedNewestToOldest || _method == BanSortMethod.ExpirationSoonestToLatest ? 1 : -1;
|
||||
}
|
||||
|
||||
if (y == null)
|
||||
{
|
||||
return _method == BanSortMethod.AddedNewestToOldest || _method == BanSortMethod.ExpirationSoonestToLatest ? -1 : 1;
|
||||
}
|
||||
|
||||
//Newest to oldest sorting uses x compared to y. Oldest to newest uses y compared to x
|
||||
return _method == BanSortMethod.AddedNewestToOldest || _method == BanSortMethod.ExpirationSoonestToLatest ? x.Value.CompareTo(y.Value)
|
||||
: y.Value.CompareTo(x.Value);
|
||||
}
|
||||
|
||||
public Ban Ban { get; set; }
|
||||
/// <summary>
|
||||
/// Compares two ban objects
|
||||
/// Whether or not the operation is valid
|
||||
/// </summary>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
/// <returns>1 if x is less than y, 0 if x is equal to y, -1 if x is greater than y</returns>
|
||||
public int Compare(Ban x, Ban y)
|
||||
{
|
||||
if (x == null)
|
||||
{
|
||||
if (y == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//If Ban y is null and Ban x is not, and we're sorting from newest to oldest, x goes before y. Else y goes before x
|
||||
return _method == BanSortMethod.AddedNewestToOldest || _method == BanSortMethod.ExpirationSoonestToLatest ? -1 : 1;
|
||||
}
|
||||
|
||||
if (x == null)
|
||||
{
|
||||
if (y == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//If Ban y is null and Ban x is not, and we're sorting from newest to oldest, x goes before y. Else y goes before x
|
||||
return _method == BanSortMethod.AddedNewestToOldest || _method == BanSortMethod.ExpirationSoonestToLatest ? -1 : 1;
|
||||
}
|
||||
|
||||
switch (_method)
|
||||
{
|
||||
case BanSortMethod.AddedNewestToOldest:
|
||||
case BanSortMethod.AddedOldestToNewest:
|
||||
return CompareDateTimes(x.BanDateTime, y.BanDateTime);
|
||||
|
||||
case BanSortMethod.ExpirationSoonestToLatest:
|
||||
case BanSortMethod.ExpirationLatestToSoonest:
|
||||
return CompareDateTimes(x.ExpirationDateTime, y.ExpirationDateTime);
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
public bool Valid { get; set; } = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -424,28 +345,32 @@ namespace TShockAPI.DB
|
|||
public class Ban
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the IP address of the ban entry.
|
||||
/// Contains constants for different identifier types known to TShock
|
||||
/// </summary>
|
||||
/// <value>The IP Address</value>
|
||||
public string IP { get; set; }
|
||||
public class Identifiers
|
||||
{
|
||||
/// <summary>
|
||||
/// IP identifier prefix constant
|
||||
/// </summary>
|
||||
public const string IP = "ip:";
|
||||
/// <summary>
|
||||
/// UUID identifier prefix constant
|
||||
/// </summary>
|
||||
public const string UUID = "uuid:";
|
||||
/// <summary>
|
||||
/// Player name identifier prefix constant
|
||||
/// </summary>
|
||||
public const string Name = "name:";
|
||||
/// <summary>
|
||||
/// User account identifier prefix constant
|
||||
/// </summary>
|
||||
public const string Account = "acc:";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// An identifiable piece of information to ban
|
||||
/// </summary>
|
||||
/// <value>The name.</value>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Client UUID of the ban
|
||||
/// </summary>
|
||||
/// <value>The UUID</value>
|
||||
public string UUID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the account name of the ban
|
||||
/// </summary>
|
||||
/// <value>The account name</value>
|
||||
public String AccountName { get; set; }
|
||||
public string Identifier { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ban reason.
|
||||
|
|
@ -460,73 +385,46 @@ namespace TShockAPI.DB
|
|||
public string BanningUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the UTC date of when the ban is to take effect
|
||||
/// DateTime from which the ban will take effect
|
||||
/// </summary>
|
||||
/// <value>The date, which must be in UTC</value>
|
||||
public string Date { get; set; }
|
||||
public DateTime BanDateTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="System.DateTime"/> object representation of the <see cref="Date"/> string.
|
||||
/// DateTime at which the ban will end
|
||||
/// </summary>
|
||||
public DateTime? BanDateTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the expiration date, in which the ban shall be lifted
|
||||
/// </summary>
|
||||
/// <value>The expiration.</value>
|
||||
public string Expiration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="System.DateTime"/> object representation of the <see cref="Expiration"/> string.
|
||||
/// </summary>
|
||||
public DateTime? ExpirationDateTime { get; }
|
||||
public DateTime ExpirationDateTime { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TShockAPI.DB.Ban"/> class.
|
||||
/// </summary>
|
||||
/// <param name="ip">Ip.</param>
|
||||
/// <param name="name">Name.</param>
|
||||
/// <param name="uuid">UUID.</param>
|
||||
/// <param name="reason">Reason.</param>
|
||||
/// <param name="banner">Banner.</param>
|
||||
/// <param name="date">UTC ban date.</param>
|
||||
/// <param name="exp">Expiration time</param>
|
||||
public Ban(string ip, string name, string uuid, string accountName, string reason, string banner, string date, string exp)
|
||||
/// <param name="identifier">Identifier to apply the ban to</param>
|
||||
/// <param name="reason">Reason for the ban</param>
|
||||
/// <param name="banningUser">Account name that executed the ban</param>
|
||||
/// <param name="start">Ban start datetime</param>
|
||||
/// <param name="end">Ban end datetime</param>
|
||||
public Ban(string identifier, string reason, string banningUser, string start, string end)
|
||||
{
|
||||
IP = ip;
|
||||
Name = name;
|
||||
UUID = uuid;
|
||||
AccountName = accountName;
|
||||
Identifier = identifier;
|
||||
Reason = reason;
|
||||
BanningUser = banner;
|
||||
Date = date;
|
||||
Expiration = exp;
|
||||
BanningUser = banningUser;
|
||||
|
||||
DateTime d;
|
||||
DateTime e;
|
||||
if (DateTime.TryParse(Date, out d))
|
||||
if (DateTime.TryParse(start, out DateTime d))
|
||||
{
|
||||
BanDateTime = d;
|
||||
}
|
||||
if (DateTime.TryParse(Expiration, out e))
|
||||
else
|
||||
{
|
||||
BanDateTime = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
if (DateTime.TryParse(end, out DateTime e))
|
||||
{
|
||||
ExpirationDateTime = e;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TShockAPI.DB.Ban"/> class.
|
||||
/// </summary>
|
||||
public Ban()
|
||||
{
|
||||
IP = string.Empty;
|
||||
Name = string.Empty;
|
||||
UUID = string.Empty;
|
||||
AccountName = string.Empty;
|
||||
Reason = string.Empty;
|
||||
BanningUser = string.Empty;
|
||||
Date = string.Empty;
|
||||
Expiration = string.Empty;
|
||||
else
|
||||
{
|
||||
ExpirationDateTime = DateTime.MaxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TShockAPI
|
||||
{
|
||||
|
|
@ -31,5 +32,25 @@ namespace TShockAPI
|
|||
foreach (T item in source)
|
||||
action(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to retrieve the value at the given index from the enumerable
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="enumerable"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public static bool TryGetValue<T>(this IEnumerable<T> enumerable, int index, out T value)
|
||||
{
|
||||
if (index < enumerable.Count())
|
||||
{
|
||||
value = enumerable.ElementAt(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3592,7 +3592,7 @@ namespace TShockAPI
|
|||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool HandleKillPortal(GetDataHandlerArgs args)
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ namespace TShockAPI
|
|||
/// <summary>
|
||||
/// Creates a new instance of <see cref="Token"/>
|
||||
/// </summary>
|
||||
public Token() : base("token", true, "The REST authentication token.", typeof(String)){}
|
||||
public Token() : base("token", true, "The REST authentication token.", typeof(String)) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -216,9 +216,13 @@ namespace TShockAPI
|
|||
Rest.RegisterRedirect("/users/update", "/v2/users/update");
|
||||
|
||||
//ban commands
|
||||
Rest.RegisterRedirect("/bans/list", "/v2/bans/list");
|
||||
Rest.RegisterRedirect("/bans/read", "/v2/bans/read");
|
||||
Rest.RegisterRedirect("/bans/destroy", "/v2/bans/destroy");
|
||||
Rest.RegisterRedirect("/bans/create", "/v3/bans/create");
|
||||
Rest.RegisterRedirect("/bans/list", "/v3/bans/list");
|
||||
Rest.RegisterRedirect("/bans/read", "/v3/bans/read");
|
||||
Rest.RegisterRedirect("/bans/destroy", "/v3/bans/destroy");
|
||||
Rest.RegisterRedirect("/v2/bans/list", "/v3/bans/list");
|
||||
Rest.RegisterRedirect("/v2/bans/read", "/v3/bans/read");
|
||||
Rest.RegisterRedirect("/v2/bans/destroy", "/v3/bans/destroy");
|
||||
|
||||
//world commands
|
||||
Rest.RegisterRedirect("/world/bloodmoon", "v3/world/bloodmoon");
|
||||
|
|
@ -258,10 +262,10 @@ namespace TShockAPI
|
|||
Rest.Register(new SecureRestCommand("/v2/users/update", UserUpdateV2, RestPermissions.restmanageusers) { DoLog = false });
|
||||
|
||||
// Ban Commands
|
||||
Rest.Register(new SecureRestCommand("/bans/create", BanCreate, RestPermissions.restmanagebans));
|
||||
Rest.Register(new SecureRestCommand("/v2/bans/list", BanListV2, RestPermissions.restviewbans));
|
||||
Rest.Register(new SecureRestCommand("/v2/bans/read", BanInfoV2, RestPermissions.restviewbans));
|
||||
Rest.Register(new SecureRestCommand("/v2/bans/destroy", BanDestroyV2, RestPermissions.restmanagebans));
|
||||
Rest.Register(new SecureRestCommand("/v3/bans/create", BanCreateV3, RestPermissions.restban, RestPermissions.restmanagebans));
|
||||
Rest.Register(new SecureRestCommand("/v3/bans/list", BanListV3, RestPermissions.restviewbans));
|
||||
Rest.Register(new SecureRestCommand("/v3/bans/read", BanInfoV3, RestPermissions.restviewbans));
|
||||
Rest.Register(new SecureRestCommand("/v3/bans/destroy", BanDestroyV3, RestPermissions.restmanagebans));
|
||||
|
||||
// World Commands
|
||||
Rest.Register(new SecureRestCommand("/world/read", WorldRead));
|
||||
|
|
@ -279,7 +283,6 @@ namespace TShockAPI
|
|||
Rest.Register(new SecureRestCommand("/v3/players/read", PlayerReadV3, RestPermissions.restuserinfo));
|
||||
Rest.Register(new SecureRestCommand("/v4/players/read", PlayerReadV4, RestPermissions.restuserinfo));
|
||||
Rest.Register(new SecureRestCommand("/v2/players/kick", PlayerKickV2, RestPermissions.restkick));
|
||||
Rest.Register(new SecureRestCommand("/v2/players/ban", PlayerBanV2, RestPermissions.restban, RestPermissions.restmanagebans));
|
||||
Rest.Register(new SecureRestCommand("/v2/players/kill", PlayerKill, RestPermissions.restkill));
|
||||
Rest.Register(new SecureRestCommand("/v2/players/mute", PlayerMute, RestPermissions.restmute));
|
||||
Rest.Register(new SecureRestCommand("/v2/players/unmute", PlayerUnMute, RestPermissions.restmute));
|
||||
|
|
@ -420,7 +423,7 @@ namespace TShockAPI
|
|||
|
||||
if (GetBool(args.Parameters["rules"], false))
|
||||
{
|
||||
var rules = new Dictionary<string,object>();
|
||||
var rules = new Dictionary<string, object>();
|
||||
rules.Add("AutoSave", TShock.Config.AutoSave);
|
||||
rules.Add("DisableBuild", TShock.Config.DisableBuild);
|
||||
rules.Add("DisableClownBombs", TShock.Config.DisableClownBombs);
|
||||
|
|
@ -492,8 +495,8 @@ namespace TShockAPI
|
|||
return RestMissingParam("user");
|
||||
|
||||
var group = args.Parameters["group"];
|
||||
if (string.IsNullOrWhiteSpace(group))
|
||||
group = TShock.Config.DefaultRegistrationGroupName;
|
||||
if (string.IsNullOrWhiteSpace(group))
|
||||
group = TShock.Config.DefaultRegistrationGroupName;
|
||||
|
||||
var password = args.Parameters["password"];
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
|
|
@ -609,115 +612,141 @@ namespace TShockAPI
|
|||
#region Rest Ban Methods
|
||||
|
||||
[Description("Create a new ban entry.")]
|
||||
[Route("/bans/create")]
|
||||
[Route("/v3/bans/create")]
|
||||
[Permission(RestPermissions.restmanagebans)]
|
||||
[Noun("ip", false, "The IP to ban, at least this or name must be specified.", typeof(String))]
|
||||
[Noun("name", false, "The name to ban, at least this or ip must be specified.", typeof(String))]
|
||||
[Noun("identifier", true, "The identifier to ban.", typeof(String))]
|
||||
[Noun("reason", false, "The reason to assign to the ban.", typeof(String))]
|
||||
[Noun("start", false, "The datetime at which the ban should start.", typeof(String))]
|
||||
[Noun("end", false, "The datetime at which the ban should end.", typeof(String))]
|
||||
[Token]
|
||||
private object BanCreate(RestRequestArgs args)
|
||||
private object BanCreateV3(RestRequestArgs args)
|
||||
{
|
||||
var ip = args.Parameters["ip"];
|
||||
var name = args.Parameters["name"];
|
||||
string identifier = args.Parameters["identifier"];
|
||||
if (string.IsNullOrWhiteSpace(identifier))
|
||||
return RestMissingParam("identifier");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ip) && string.IsNullOrWhiteSpace(name))
|
||||
return RestMissingParam("ip", "name");
|
||||
string reason = args.Parameters["reason"];
|
||||
if (string.IsNullOrWhiteSpace(reason))
|
||||
reason = "Banned";
|
||||
|
||||
try
|
||||
if (!DateTime.TryParse(args.Parameters["start"], out DateTime startDate))
|
||||
startDate = DateTime.UtcNow;
|
||||
|
||||
if (!DateTime.TryParse(args.Parameters["end"], out DateTime endDate))
|
||||
endDate = DateTime.MaxValue;
|
||||
|
||||
if (TShock.Bans.InsertBan(identifier, reason, args.TokenData.Username, startDate, endDate) != null)
|
||||
{
|
||||
TShock.Bans.AddBan(ip, name, "", "", args.Parameters["reason"], true, args.TokenData.Username);
|
||||
TSPlayer player = null;
|
||||
if (identifier.StartsWith(Ban.Identifiers.IP))
|
||||
{
|
||||
player = TShock.Players.FirstOrDefault(p => p.IP == identifier.Substring(Ban.Identifiers.IP.Length));
|
||||
}
|
||||
else if (identifier.StartsWith(Ban.Identifiers.Name))
|
||||
{
|
||||
//Character names may not necessarily be unique, so kick all matches
|
||||
foreach (var ply in TShock.Players.Where(p => p.Name == identifier.Substring(Ban.Identifiers.Name.Length)))
|
||||
{
|
||||
ply.Kick(reason, true);
|
||||
}
|
||||
}
|
||||
else if (identifier.StartsWith(Ban.Identifiers.Account))
|
||||
{
|
||||
player = TShock.Players.FirstOrDefault(p => p.Account?.Name == identifier.Substring(Ban.Identifiers.Account.Length));
|
||||
}
|
||||
else if (identifier.StartsWith(Ban.Identifiers.UUID))
|
||||
{
|
||||
player = TShock.Players.FirstOrDefault(p => p.UUID == identifier.Substring(Ban.Identifiers.UUID.Length));
|
||||
}
|
||||
|
||||
if (player != null)
|
||||
{
|
||||
player.Kick(reason, true);
|
||||
}
|
||||
|
||||
return RestResponse("Ban added.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return RestError(e.Message);
|
||||
}
|
||||
return RestResponse("Ban created successfully");
|
||||
|
||||
return RestError("Failed to add ban.", status: "500");
|
||||
}
|
||||
|
||||
[Description("Delete an existing ban entry.")]
|
||||
[Route("/v2/bans/destroy")]
|
||||
[Route("/v3/bans/destroy")]
|
||||
[Permission(RestPermissions.restmanagebans)]
|
||||
[Noun("ban", true, "The search criteria, either an IP address or a name.", typeof(String))]
|
||||
[Noun("type", true, "The type of search criteria, 'ip' or 'name'. Also used as the method of removing from the database.", typeof(String))]
|
||||
[Noun("caseinsensitive", false, "Name lookups should be case insensitive.", typeof(bool))]
|
||||
[Noun("identifier", true, "The identifier of the ban to delete.", typeof(String))]
|
||||
[Noun("fullDelete", false, "Whether or not to completely remove the ban from the system.", typeof(bool))]
|
||||
[Token]
|
||||
private object BanDestroyV2(RestRequestArgs args)
|
||||
private object BanDestroyV3(RestRequestArgs args)
|
||||
{
|
||||
var ret = BanFind(args.Parameters);
|
||||
if (ret is RestObject)
|
||||
return ret;
|
||||
string identifier = args.Parameters["identifier"];
|
||||
if (string.IsNullOrWhiteSpace(identifier))
|
||||
return RestMissingParam("identifier");
|
||||
|
||||
try
|
||||
{
|
||||
Ban ban = (Ban)ret;
|
||||
switch (args.Parameters["type"])
|
||||
{
|
||||
case "ip":
|
||||
if (!TShock.Bans.RemoveBan(ban.IP, false, false, true))
|
||||
return RestResponse("Failed to delete ban (already deleted?)");
|
||||
break;
|
||||
case "name":
|
||||
if (!TShock.Bans.RemoveBan(ban.Name, true, GetBool(args.Parameters["caseinsensitive"], true)))
|
||||
return RestResponse("Failed to delete ban (already deleted?)");
|
||||
break;
|
||||
default:
|
||||
return RestError("Invalid Type: '" + args.Parameters["type"] + "'");
|
||||
}
|
||||
bool.TryParse(args.Parameters["fullDelete"], out bool fullDelete);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
if (TShock.Bans.RemoveBan(identifier, fullDelete))
|
||||
{
|
||||
return RestError(e.Message);
|
||||
return RestResponse("Ban removed.");
|
||||
}
|
||||
|
||||
return RestResponse("Ban deleted successfully");
|
||||
return RestError("Failed to remove ban.", status: "500");
|
||||
}
|
||||
|
||||
[Description("View the details of a specific ban.")]
|
||||
[Route("/v2/bans/read")]
|
||||
[Route("/v3/bans/read")]
|
||||
[Permission(RestPermissions.restviewbans)]
|
||||
[Noun("ban", true, "The search criteria, either an IP address or a name.", typeof(String))]
|
||||
[Noun("type", true, "The type of search criteria, 'ip' or 'name'.", typeof(String))]
|
||||
[Noun("caseinsensitive", false, "Name lookups should be case insensitive.", typeof(bool))]
|
||||
[Noun("identifier", true, "The identifier to search for.", typeof(String))]
|
||||
[Token]
|
||||
private object BanInfoV2(RestRequestArgs args)
|
||||
private object BanInfoV3(RestRequestArgs args)
|
||||
{
|
||||
var ret = BanFind(args.Parameters);
|
||||
if (ret is RestObject)
|
||||
return ret;
|
||||
string identifier = args.Parameters["identifier"];
|
||||
if (string.IsNullOrWhiteSpace(identifier))
|
||||
return RestMissingParam("identifier");
|
||||
|
||||
Ban ban = (Ban)ret;
|
||||
return new RestObject() {
|
||||
{"name", null == ban.Name ? "" : ban.Name},
|
||||
{"ip", null == ban.IP ? "" : ban.IP},
|
||||
{"banning_user", null == ban.BanningUser ? "" : ban.BanningUser},
|
||||
{"date", null == ban.BanDateTime ? "" : ban.BanDateTime.Value.ToString()},
|
||||
{"reason", null == ban.Reason ? "" : ban.Reason},
|
||||
Ban ban = TShock.Bans.GetBanByIdentifier(identifier);
|
||||
|
||||
if (ban == null)
|
||||
{
|
||||
return RestResponse("No matching bans found.");
|
||||
}
|
||||
|
||||
return new RestObject
|
||||
{
|
||||
{"identifier", ban.Identifier },
|
||||
{"reason", ban.Reason },
|
||||
{"banning_user", ban.BanningUser },
|
||||
{"fromDate", ban.BanDateTime.ToString("s") },
|
||||
{"toDate", ban.ExpirationDateTime.ToString("s") },
|
||||
};
|
||||
}
|
||||
|
||||
[Description("View all bans in the TShock database.")]
|
||||
[Route("/v2/bans/list")]
|
||||
[Route("/v3/bans/list")]
|
||||
[Permission(RestPermissions.restviewbans)]
|
||||
[Token]
|
||||
private object BanListV2(RestRequestArgs args)
|
||||
private object BanListV3(RestRequestArgs args)
|
||||
{
|
||||
IEnumerable<Ban> bans = TShock.Bans.GetAllBans();
|
||||
|
||||
var banList = new ArrayList();
|
||||
foreach (var ban in TShock.Bans.GetBans())
|
||||
foreach (var ban in bans)
|
||||
{
|
||||
banList.Add(
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{"name", null == ban.Name ? "" : ban.Name},
|
||||
{"ip", null == ban.IP ? "" : ban.IP},
|
||||
{"banning_user", null == ban.BanningUser ? "" : ban.BanningUser},
|
||||
{"date", null == ban.BanDateTime ? "" : ban.BanDateTime.Value.ToString()},
|
||||
{"reason", null == ban.Reason ? "" : ban.Reason},
|
||||
{"identifier", ban.Identifier },
|
||||
{"reason", ban.Reason },
|
||||
{"banning_user", ban.BanningUser },
|
||||
{"fromDate", ban.BanDateTime.ToString("s") },
|
||||
{"toDate", ban.ExpirationDateTime.ToString("s") },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return new RestObject() { { "bans", banList } };
|
||||
return new RestObject
|
||||
{
|
||||
{ "bans", banList }
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -987,26 +1016,6 @@ namespace TShockAPI
|
|||
return RestResponse("Player " + player.Name + " was kicked");
|
||||
}
|
||||
|
||||
[Description("Add a ban to the database.")]
|
||||
[Route("/v2/players/ban")]
|
||||
[Permission(RestPermissions.restban)]
|
||||
[Permission(RestPermissions.restmanagebans)]
|
||||
[Noun("player", true, "The player to kick.", typeof(String))]
|
||||
[Noun("reason", false, "The reason the user was banned.", typeof(String))]
|
||||
[Token]
|
||||
private object PlayerBanV2(RestRequestArgs args)
|
||||
{
|
||||
var ret = PlayerFind(args.Parameters);
|
||||
if (ret is RestObject)
|
||||
return ret;
|
||||
|
||||
TSPlayer player = (TSPlayer)ret;
|
||||
var reason = null == args.Parameters["reason"] ? "Banned via web" : args.Parameters["reason"];
|
||||
TShock.Bans.AddBan(player.IP, player.Name, "", "", reason);
|
||||
player.Kick(reason, true, false, null, true);
|
||||
return RestResponse("Player " + player.Name + " was banned");
|
||||
}
|
||||
|
||||
[Description("Kill a player.")]
|
||||
[Route("/v2/players/kill")]
|
||||
[Permission(RestPermissions.restkill)]
|
||||
|
|
@ -1039,7 +1048,7 @@ namespace TShockAPI
|
|||
var groups = new ArrayList();
|
||||
foreach (Group group in TShock.Groups)
|
||||
{
|
||||
groups.Add(new Dictionary<string, object> {{"name", group.Name}, {"parent", group.ParentName}, {"chatcolor", group.ChatColor}});
|
||||
groups.Add(new Dictionary<string, object> { { "name", group.Name }, { "parent", group.ParentName }, { "chatcolor", group.ChatColor } });
|
||||
}
|
||||
return new RestObject() { { "groups", groups } };
|
||||
}
|
||||
|
|
@ -1200,7 +1209,7 @@ namespace TShockAPI
|
|||
}
|
||||
}
|
||||
sb.AppendLine("Example Usage: {0}?{1}".SFormat(routeattr.Route,
|
||||
string.Join("&", nouns.Select(n => String.Format("{0}={0}", ((Noun) n).Name)))));
|
||||
string.Join("&", nouns.Select(n => String.Format("{0}={0}", ((Noun)n).Name)))));
|
||||
sb.AppendLine();
|
||||
}
|
||||
}
|
||||
|
|
@ -1210,12 +1219,12 @@ namespace TShockAPI
|
|||
|
||||
private RestObject RestError(string message, string status = "400")
|
||||
{
|
||||
return new RestObject(status) {Error = message};
|
||||
return new RestObject(status) { Error = message };
|
||||
}
|
||||
|
||||
private RestObject RestResponse(string message, string status = "200")
|
||||
{
|
||||
return new RestObject(status) {Response = message};
|
||||
return new RestObject(status) { Response = message };
|
||||
}
|
||||
|
||||
private RestObject RestMissingParam(string var)
|
||||
|
|
@ -1246,7 +1255,7 @@ namespace TShockAPI
|
|||
return RestMissingParam("player");
|
||||
|
||||
var found = TSPlayer.FindByNameOrID(name);
|
||||
switch(found.Count)
|
||||
switch (found.Count)
|
||||
{
|
||||
case 1:
|
||||
return found[0];
|
||||
|
|
@ -1292,35 +1301,6 @@ namespace TShockAPI
|
|||
return account;
|
||||
}
|
||||
|
||||
private object BanFind(IParameterCollection parameters)
|
||||
{
|
||||
string name = parameters["ban"];
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
return RestMissingParam("ban");
|
||||
|
||||
string type = parameters["type"];
|
||||
if (string.IsNullOrWhiteSpace(type))
|
||||
return RestMissingParam("type");
|
||||
|
||||
Ban ban;
|
||||
switch (type)
|
||||
{
|
||||
case "ip":
|
||||
ban = TShock.Bans.GetBanByIp(name);
|
||||
break;
|
||||
case "name":
|
||||
ban = TShock.Bans.GetBanByName(name, GetBool(parameters["caseinsensitive"], true));
|
||||
break;
|
||||
default:
|
||||
return RestError("Invalid Type: '" + type + "'");
|
||||
}
|
||||
|
||||
if (null == ban)
|
||||
return RestError("Ban " + type + " '" + name + "' doesn't exist");
|
||||
|
||||
return ban;
|
||||
}
|
||||
|
||||
private object GroupFind(IParameterCollection parameters)
|
||||
{
|
||||
var name = parameters["group"];
|
||||
|
|
|
|||
|
|
@ -1638,9 +1638,13 @@ namespace TShockAPI
|
|||
return true;
|
||||
if (force || !HasPermission(Permissions.immunetoban))
|
||||
{
|
||||
string ip = IP;
|
||||
string uuid = UUID;
|
||||
TShock.Bans.AddBan(ip, Name, uuid, "", reason, false, adminUserName);
|
||||
TShock.Bans.InsertBan($"{DB.Ban.Identifiers.IP}{IP}", reason, adminUserName, DateTime.UtcNow, DateTime.MaxValue);
|
||||
TShock.Bans.InsertBan($"{DB.Ban.Identifiers.IP}{UUID}", reason, adminUserName, DateTime.UtcNow, DateTime.MaxValue);
|
||||
if (Account != null)
|
||||
{
|
||||
TShock.Bans.InsertBan($"{DB.Ban.Identifiers.Account}{Account.Name}", reason, adminUserName, DateTime.UtcNow, DateTime.MaxValue);
|
||||
}
|
||||
|
||||
Disconnect(string.Format("Banned: {0}", reason));
|
||||
string verb = force ? "force " : "";
|
||||
if (string.IsNullOrWhiteSpace(adminUserName))
|
||||
|
|
|
|||
|
|
@ -478,26 +478,13 @@ namespace TShockAPI
|
|||
args.Player.Account.KnownIps = JsonConvert.SerializeObject(KnownIps, Formatting.Indented);
|
||||
UserAccounts.UpdateLogin(args.Player.Account);
|
||||
|
||||
Ban potentialBan = Bans.GetBanByAccountName(args.Player.Account.Name);
|
||||
//Check if this user has any recorded bans
|
||||
var validBan = Bans.GetBansByIdentifiers($"acc:{args.Player.Account.Name}", $"uuid:{args.Player.UUID}").FirstOrDefault(b => Bans.IsValidBan(b));
|
||||
|
||||
if (potentialBan != null)
|
||||
//If they do and any are still valid, kick them
|
||||
if (validBan != null)
|
||||
{
|
||||
// A user just signed in successfully despite being banned by account name.
|
||||
// We should fix the ban database so that all of their ban info is up to date.
|
||||
Bans.AddBan(args.Player.IP, args.Player.Name, args.Player.UUID, args.Player.Account.Name,
|
||||
potentialBan.Reason, false, potentialBan.BanningUser, potentialBan.Expiration);
|
||||
|
||||
// And then get rid of them.
|
||||
if (potentialBan.Expiration == "")
|
||||
{
|
||||
args.Player.Kick(String.Format("Permanently banned by {0} for {1}", potentialBan.BanningUser
|
||||
,potentialBan.Reason), true, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Player.Kick(String.Format("Still banned by {0} for {1}", potentialBan.BanningUser,
|
||||
potentialBan.Reason), true, true);
|
||||
}
|
||||
args.Player.Kick($"You are banned: {validBan.Reason}", true, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -816,7 +803,7 @@ namespace TShockAPI
|
|||
Console.WriteLine("Startup parameter overrode REST port.");
|
||||
}
|
||||
})
|
||||
.AddFlags(playerSet, (p)=>
|
||||
.AddFlags(playerSet, (p) =>
|
||||
{
|
||||
int slots;
|
||||
if (int.TryParse(p, out slots))
|
||||
|
|
@ -1100,7 +1087,7 @@ namespace TShockAPI
|
|||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if(!OnCreep(args.Grass))
|
||||
if (!OnCreep(args.Grass))
|
||||
{
|
||||
args.Handled = true;
|
||||
}
|
||||
|
|
@ -1209,65 +1196,45 @@ namespace TShockAPI
|
|||
return;
|
||||
}
|
||||
|
||||
Ban ban = null;
|
||||
if (Config.EnableBanOnUsernames)
|
||||
{
|
||||
var newban = Bans.GetBanByName(player.Name);
|
||||
if (null != newban)
|
||||
ban = newban;
|
||||
}
|
||||
|
||||
if (Config.EnableIPBans && null == ban)
|
||||
{
|
||||
ban = Bans.GetBanByIp(player.IP);
|
||||
}
|
||||
|
||||
if (Config.EnableUUIDBans && null == ban && !String.IsNullOrWhiteSpace(player.UUID))
|
||||
{
|
||||
ban = Bans.GetBanByUUID(player.UUID);
|
||||
}
|
||||
Ban ban = Bans.GetBansByIdentifiers($"name:{player.Name}", $"uuid:{player.UUID}", $"ip:{player.IP}").FirstOrDefault(b => Bans.IsValidBan(b));
|
||||
|
||||
if (ban != null)
|
||||
{
|
||||
if (!Bans.RemoveBanIfExpired(ban))
|
||||
if (ban.ExpirationDateTime == DateTime.MaxValue)
|
||||
{
|
||||
DateTime exp;
|
||||
if (!DateTime.TryParse(ban.Expiration, out exp))
|
||||
player.Disconnect("You are banned: " + ban.Reason);
|
||||
}
|
||||
else
|
||||
{
|
||||
TimeSpan ts = ban.ExpirationDateTime - DateTime.UtcNow;
|
||||
int months = ts.Days / 30;
|
||||
if (months > 0)
|
||||
{
|
||||
player.Disconnect("Permanently banned for: " + ban.Reason);
|
||||
player.Disconnect(String.Format("You are banned for {0} month{1} and {2} day{3}: {4}",
|
||||
months, months == 1 ? "" : "s", ts.Days, ts.Days == 1 ? "" : "s", ban.Reason));
|
||||
}
|
||||
else if (ts.Days > 0)
|
||||
{
|
||||
player.Disconnect(String.Format("You are banned for {0} day{1} and {2} hour{3}: {4}",
|
||||
ts.Days, ts.Days == 1 ? "" : "s", ts.Hours, ts.Hours == 1 ? "" : "s", ban.Reason));
|
||||
}
|
||||
else if (ts.Hours > 0)
|
||||
{
|
||||
player.Disconnect(String.Format("You are banned for {0} hour{1} and {2} minute{3}: {4}",
|
||||
ts.Hours, ts.Hours == 1 ? "" : "s", ts.Minutes, ts.Minutes == 1 ? "" : "s", ban.Reason));
|
||||
}
|
||||
else if (ts.Minutes > 0)
|
||||
{
|
||||
player.Disconnect(String.Format("You are banned for {0} minute{1} and {2} second{3}: {4}",
|
||||
ts.Minutes, ts.Minutes == 1 ? "" : "s", ts.Seconds, ts.Seconds == 1 ? "" : "s", ban.Reason));
|
||||
}
|
||||
else
|
||||
{
|
||||
TimeSpan ts = exp - DateTime.UtcNow;
|
||||
int months = ts.Days / 30;
|
||||
if (months > 0)
|
||||
{
|
||||
player.Disconnect(String.Format("You are banned for {0} month{1} and {2} day{3}: {4}",
|
||||
months, months == 1 ? "" : "s", ts.Days, ts.Days == 1 ? "" : "s", ban.Reason));
|
||||
}
|
||||
else if (ts.Days > 0)
|
||||
{
|
||||
player.Disconnect(String.Format("You are banned for {0} day{1} and {2} hour{3}: {4}",
|
||||
ts.Days, ts.Days == 1 ? "" : "s", ts.Hours, ts.Hours == 1 ? "" : "s", ban.Reason));
|
||||
}
|
||||
else if (ts.Hours > 0)
|
||||
{
|
||||
player.Disconnect(String.Format("You are banned for {0} hour{1} and {2} minute{3}: {4}",
|
||||
ts.Hours, ts.Hours == 1 ? "" : "s", ts.Minutes, ts.Minutes == 1 ? "" : "s", ban.Reason));
|
||||
}
|
||||
else if (ts.Minutes > 0)
|
||||
{
|
||||
player.Disconnect(String.Format("You are banned for {0} minute{1} and {2} second{3}: {4}",
|
||||
ts.Minutes, ts.Minutes == 1 ? "" : "s", ts.Seconds, ts.Seconds == 1 ? "" : "s", ban.Reason));
|
||||
}
|
||||
else
|
||||
{
|
||||
player.Disconnect(String.Format("You are banned for {0} second{1}: {2}",
|
||||
ts.Seconds, ts.Seconds == 1 ? "" : "s", ban.Reason));
|
||||
}
|
||||
player.Disconnect(String.Format("You are banned for {0} second{1}: {2}",
|
||||
ts.Seconds, ts.Seconds == 1 ? "" : "s", ban.Reason));
|
||||
}
|
||||
args.Handled = true;
|
||||
}
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1647,7 +1614,8 @@ namespace TShockAPI
|
|||
player.RecentlyCreatedProjectiles.RemoveAll(p => p.Index == e.number && p.Killed);
|
||||
}
|
||||
|
||||
if (!player.RecentlyCreatedProjectiles.Any(p => p.Index == e.number)) {
|
||||
if (!player.RecentlyCreatedProjectiles.Any(p => p.Index == e.number))
|
||||
{
|
||||
player.RecentlyCreatedProjectiles.Add(new GetDataHandlers.ProjectileStruct()
|
||||
{
|
||||
Index = e.number,
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@
|
|||
</PropertyGroup>
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<UserProperties BuildVersion_UpdateAssemblyVersion="True" BuildVersion_UpdateFileVersion="True" BuildVersion_BuildAction="Both" BuildVersion_BuildVersioningStyle="None.None.None.MonthAndDayStamp" BuildVersion_StartDate="2011/6/17" BuildVersion_IncrementBeforeBuild="False" />
|
||||
<UserProperties BuildVersion_IncrementBeforeBuild="False" BuildVersion_StartDate="2011/6/17" BuildVersion_BuildVersioningStyle="None.None.None.MonthAndDayStamp" BuildVersion_BuildAction="Both" BuildVersion_UpdateFileVersion="True" BuildVersion_UpdateAssemblyVersion="True" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
|
|
|||
|
|
@ -531,6 +531,11 @@ namespace TShockAPI
|
|||
{
|
||||
seconds = 0;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var sb = new StringBuilder(3);
|
||||
for (int i = 0; i < str.Length; i++)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue