diff --git a/SharpChat/ChatContext.cs b/SharpChat/ChatContext.cs index 09fd9d0..c9d2f72 100644 --- a/SharpChat/ChatContext.cs +++ b/SharpChat/ChatContext.cs @@ -285,7 +285,7 @@ namespace SharpChat { if(!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.IsOwner(user)) { if(chan.Rank > user.Rank) { - SendTo(user, new LegacyCommandResponse(LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name)); + SendTo(user, new LegacyCommandResponse(LCR.CHANNEL_INSUFFICIENT_RANK, true, chan.Name)); ForceChannel(user); return; } diff --git a/SharpChat/Commands/BanListCommand.cs b/SharpChat/Commands/BanListCommand.cs index a986d2c..0a5ccf7 100644 --- a/SharpChat/Commands/BanListCommand.cs +++ b/SharpChat/Commands/BanListCommand.cs @@ -19,7 +19,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } diff --git a/SharpChat/Commands/BroadcastCommand.cs b/SharpChat/Commands/BroadcastCommand.cs index 2c86929..720957f 100644 --- a/SharpChat/Commands/BroadcastCommand.cs +++ b/SharpChat/Commands/BroadcastCommand.cs @@ -11,7 +11,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.Broadcast)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } diff --git a/SharpChat/Commands/CreateChannelCommand.cs b/SharpChat/Commands/CreateChannelCommand.cs index 21b2339..c90cd45 100644 --- a/SharpChat/Commands/CreateChannelCommand.cs +++ b/SharpChat/Commands/CreateChannelCommand.cs @@ -9,7 +9,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.CreateChannel)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } @@ -17,7 +17,7 @@ namespace SharpChat.Commands { bool createChanHasHierarchy; if(!ctx.Args.Any() || (createChanHasHierarchy = firstArg.All(char.IsDigit) && ctx.Args.Length < 2)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new CommandFormatErrorPacket()); return; } @@ -27,7 +27,7 @@ namespace SharpChat.Commands { createChanHierarchy = 0; if(createChanHierarchy > ctx.User.Rank) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.INSUFFICIENT_HIERARCHY)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.INSUFFICIENT_RANK)); return; } diff --git a/SharpChat/Commands/DeleteChannelCommand.cs b/SharpChat/Commands/DeleteChannelCommand.cs index f9e024b..9fb5a9d 100644 --- a/SharpChat/Commands/DeleteChannelCommand.cs +++ b/SharpChat/Commands/DeleteChannelCommand.cs @@ -12,7 +12,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.Args.Any() || string.IsNullOrWhiteSpace(ctx.Args.FirstOrDefault())) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new CommandFormatErrorPacket()); return; } diff --git a/SharpChat/Commands/DeleteMessageCommand.cs b/SharpChat/Commands/DeleteMessageCommand.cs index a85e12f..100fab5 100644 --- a/SharpChat/Commands/DeleteMessageCommand.cs +++ b/SharpChat/Commands/DeleteMessageCommand.cs @@ -16,14 +16,14 @@ namespace SharpChat.Commands bool deleteAnyMessage = ctx.User.Can(ChatUserPermissions.DeleteAnyMessage); if(!deleteAnyMessage && !ctx.User.Can(ChatUserPermissions.DeleteOwnMessage)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } string? firstArg = ctx.Args.FirstOrDefault(); if(string.IsNullOrWhiteSpace(firstArg) || !firstArg.All(char.IsDigit) || !long.TryParse(firstArg, out long delSeqId)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new CommandFormatErrorPacket()); return; } diff --git a/SharpChat/Commands/KickBanCommand.cs b/SharpChat/Commands/KickBanCommand.cs index acde202..5aee6e0 100644 --- a/SharpChat/Commands/KickBanCommand.cs +++ b/SharpChat/Commands/KickBanCommand.cs @@ -21,7 +21,7 @@ namespace SharpChat.Commands { bool isBanning = ctx.NameEquals("ban"); if(!ctx.User.Can(isBanning ? ChatUserPermissions.BanUser : ChatUserPermissions.KickUser)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } @@ -43,7 +43,7 @@ namespace SharpChat.Commands { TimeSpan duration = isBanning ? TimeSpan.MaxValue : TimeSpan.Zero; if(!string.IsNullOrWhiteSpace(banDurationStr) && double.TryParse(banDurationStr, out double durationSeconds)) { if(durationSeconds < 0) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new CommandFormatErrorPacket()); return; } diff --git a/SharpChat/Commands/NickCommand.cs b/SharpChat/Commands/NickCommand.cs index f9e61bd..b4bd7f6 100644 --- a/SharpChat/Commands/NickCommand.cs +++ b/SharpChat/Commands/NickCommand.cs @@ -11,7 +11,7 @@ namespace SharpChat.Commands { bool setOthersNick = ctx.User.Can(ChatUserPermissions.SetOthersNickname); if(!setOthersNick && !ctx.User.Can(ChatUserPermissions.SetOwnNickname)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } @@ -26,7 +26,7 @@ namespace SharpChat.Commands { targetUser ??= ctx.User; if(ctx.Args.Length < offset) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new CommandFormatErrorPacket()); return; } diff --git a/SharpChat/Commands/PardonAddressCommand.cs b/SharpChat/Commands/PardonAddressCommand.cs index 4acb5f2..2f67eda 100644 --- a/SharpChat/Commands/PardonAddressCommand.cs +++ b/SharpChat/Commands/PardonAddressCommand.cs @@ -20,13 +20,13 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } string? unbanAddrTarget = ctx.Args.FirstOrDefault(); if(string.IsNullOrWhiteSpace(unbanAddrTarget) || !IPAddress.TryParse(unbanAddrTarget, out IPAddress? unbanAddr)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new CommandFormatErrorPacket()); return; } diff --git a/SharpChat/Commands/PardonUserCommand.cs b/SharpChat/Commands/PardonUserCommand.cs index 3adeaca..455efa9 100644 --- a/SharpChat/Commands/PardonUserCommand.cs +++ b/SharpChat/Commands/PardonUserCommand.cs @@ -19,14 +19,14 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.BanUser | ChatUserPermissions.KickUser)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } bool unbanUserTargetIsName = true; string? unbanUserTarget = ctx.Args.FirstOrDefault(); if(string.IsNullOrWhiteSpace(unbanUserTarget)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new CommandFormatErrorPacket()); return; } diff --git a/SharpChat/Commands/PasswordChannelCommand.cs b/SharpChat/Commands/PasswordChannelCommand.cs index 24700dc..8327569 100644 --- a/SharpChat/Commands/PasswordChannelCommand.cs +++ b/SharpChat/Commands/PasswordChannelCommand.cs @@ -9,7 +9,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.SetChannelPassword) || ctx.Channel.IsOwner(ctx.User)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } diff --git a/SharpChat/Commands/RankChannelCommand.cs b/SharpChat/Commands/RankChannelCommand.cs index 4caab19..536a3af 100644 --- a/SharpChat/Commands/RankChannelCommand.cs +++ b/SharpChat/Commands/RankChannelCommand.cs @@ -11,17 +11,17 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.SetChannelHierarchy) || ctx.Channel.IsOwner(ctx.User)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } if(!ctx.Args.Any() || !int.TryParse(ctx.Args.First(), out int chanHierarchy) || chanHierarchy > ctx.User.Rank) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.INSUFFICIENT_HIERARCHY)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.INSUFFICIENT_RANK)); return; } ctx.Chat.UpdateChannel(ctx.Channel, minRank: chanHierarchy); - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_HIERARCHY_CHANGED, false)); + ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.CHANNEL_RANK_CHANGED, false)); } } } diff --git a/SharpChat/Commands/ShutdownRestartCommand.cs b/SharpChat/Commands/ShutdownRestartCommand.cs index ce5ab27..f2746aa 100644 --- a/SharpChat/Commands/ShutdownRestartCommand.cs +++ b/SharpChat/Commands/ShutdownRestartCommand.cs @@ -19,7 +19,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(ctx.User.UserId != 1) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, $"/{ctx.Name}")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } diff --git a/SharpChat/Commands/WhisperCommand.cs b/SharpChat/Commands/WhisperCommand.cs index 3117e64..17496ce 100644 --- a/SharpChat/Commands/WhisperCommand.cs +++ b/SharpChat/Commands/WhisperCommand.cs @@ -12,7 +12,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(ctx.Args.Length < 2) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_FORMAT_ERROR)); + ctx.Chat.SendTo(ctx.User, new CommandFormatErrorPacket()); return; } diff --git a/SharpChat/Commands/RemoteAddressCommand.cs b/SharpChat/Commands/WhoisCommand.cs similarity index 76% rename from SharpChat/Commands/RemoteAddressCommand.cs rename to SharpChat/Commands/WhoisCommand.cs index b579c5b..5f61f7d 100644 --- a/SharpChat/Commands/RemoteAddressCommand.cs +++ b/SharpChat/Commands/WhoisCommand.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Net; namespace SharpChat.Commands { - public class RemoteAddressCommand : IChatCommand { + public class WhoisCommand : IChatCommand { public bool IsMatch(ChatCommandContext ctx) { return ctx.NameEquals("ip") || ctx.NameEquals("whois"); @@ -11,7 +11,7 @@ namespace SharpChat.Commands { public void Dispatch(ChatCommandContext ctx) { if(!ctx.User.Can(ChatUserPermissions.SeeIPAddress)) { - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.COMMAND_NOT_ALLOWED, true, "/ip")); + ctx.Chat.SendTo(ctx.User, new CommandNotAllowedErrorPacket(ctx.Name)); return; } @@ -24,7 +24,7 @@ namespace SharpChat.Commands { } foreach(IPAddress ip in ctx.Chat.GetRemoteAddresses(ipUser)) - ctx.Chat.SendTo(ctx.User, new LegacyCommandResponse(LCR.IP_ADDRESS, false, ipUser.UserName, ip)); + ctx.Chat.SendTo(ctx.User, new WhoisResponsePacket(ipUser.UserName, ip.ToString())); } } } diff --git a/SharpChat/Packet/CommandFormatErrorPacket.cs b/SharpChat/Packet/CommandFormatErrorPacket.cs new file mode 100644 index 0000000..37e0d24 --- /dev/null +++ b/SharpChat/Packet/CommandFormatErrorPacket.cs @@ -0,0 +1,15 @@ +using System; + +namespace SharpChat.Packet { + public class CommandFormatErrorPacket : ServerPacket { + private readonly long Timestamp; + + public CommandFormatErrorPacket() { + Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); + } + + public override string Pack() { + return string.Format("2\t{0}\t-1\t1\fcmdna\t{1}\t10010", Timestamp, SequenceId); + } + } +} diff --git a/SharpChat/Packet/CommandNotAllowedErrorPacket.cs b/SharpChat/Packet/CommandNotAllowedErrorPacket.cs new file mode 100644 index 0000000..3b7f228 --- /dev/null +++ b/SharpChat/Packet/CommandNotAllowedErrorPacket.cs @@ -0,0 +1,17 @@ +using System; + +namespace SharpChat.Packet { + public class CommandNotAllowedErrorPacket : ServerPacket { + private readonly long Timestamp; + private readonly string CommandName; + + public CommandNotAllowedErrorPacket(string commandName) { + Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); + CommandName = commandName; + } + + public override string Pack() { + return string.Format("2\t{0}\t-1\t1\fcmdna\f/{1}\t{2}\t10010", Timestamp, CommandName, SequenceId); + } + } +} diff --git a/SharpChat/Packet/FloodWarningPacket.cs b/SharpChat/Packet/FloodWarningPacket.cs new file mode 100644 index 0000000..813da85 --- /dev/null +++ b/SharpChat/Packet/FloodWarningPacket.cs @@ -0,0 +1,15 @@ +using System; + +namespace SharpChat.Packet { + public class FloodWarningPacket : ServerPacket { + private readonly long Timestamp; + + public FloodWarningPacket() { + Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); + } + + public override string Pack() { + return string.Format("2\t{0}\t-1\t0\fflwarn\t{1}\t10010", Timestamp, SequenceId); + } + } +} diff --git a/SharpChat/Packet/LegacyCommandResponse.cs b/SharpChat/Packet/LegacyCommandResponse.cs index 617c3ba..05ea562 100644 --- a/SharpChat/Packet/LegacyCommandResponse.cs +++ b/SharpChat/Packet/LegacyCommandResponse.cs @@ -38,29 +38,26 @@ namespace SharpChat.Packet { // Abbreviated class name because otherwise shit gets wide public static class LCR { - public const string COMMAND_NOT_ALLOWED = "cmdna"; - public const string COMMAND_FORMAT_ERROR = "cmderr"; - public const string IP_ADDRESS = "ipaddr"; + public const string CHANNEL_CREATED = "crchan"; + public const string CHANNEL_DELETED = "delchan"; + public const string CHANNEL_PASSWORD_CHANGED = "cpwdchan"; + public const string CHANNEL_RANK_CHANGED = "cprivchan"; + public const string USERS_LISTING_CHANNEL = "whochan"; + public const string USERS_LISTING_SERVER = "who"; + public const string USER_UNBANNED = "unban"; + public const string USER_NOT_FOUND = "usernf"; public const string NAME_IN_USE = "nameinuse"; - public const string CHANNEL_INSUFFICIENT_HIERARCHY = "ipchan"; + public const string CHANNEL_INSUFFICIENT_RANK = "ipchan"; public const string CHANNEL_INVALID_PASSWORD = "ipwchan"; public const string CHANNEL_NOT_FOUND = "nochan"; public const string CHANNEL_ALREADY_EXISTS = "nischan"; public const string CHANNEL_NAME_INVALID = "inchan"; - public const string CHANNEL_CREATED = "crchan"; public const string CHANNEL_DELETE_FAILED = "ndchan"; - public const string CHANNEL_DELETED = "delchan"; - public const string CHANNEL_PASSWORD_CHANGED = "cpwdchan"; - public const string CHANNEL_HIERARCHY_CHANGED = "cprivchan"; public const string USERS_LISTING_ERROR = "whoerr"; - public const string USERS_LISTING_CHANNEL = "whochan"; - public const string USERS_LISTING_SERVER = "who"; - public const string INSUFFICIENT_HIERARCHY = "rankerr"; + public const string INSUFFICIENT_RANK = "rankerr"; public const string MESSAGE_DELETE_ERROR = "delerr"; public const string KICK_NOT_ALLOWED = "kickna"; public const string USER_NOT_BANNED = "notban"; - public const string USER_UNBANNED = "unban"; - public const string FLOOD_WARN = "flwarn"; } } diff --git a/SharpChat/Packet/WhoisResponsePacket.cs b/SharpChat/Packet/WhoisResponsePacket.cs new file mode 100644 index 0000000..428e454 --- /dev/null +++ b/SharpChat/Packet/WhoisResponsePacket.cs @@ -0,0 +1,19 @@ +using System; + +namespace SharpChat.Packet { + public class WhoisResponsePacket : ServerPacket { + private readonly long Timestamp; + private readonly string UserName; + private readonly string RemoteAddress; + + public WhoisResponsePacket(string userName, string remoteAddress) { + Timestamp = DateTimeOffset.Now.ToUnixTimeSeconds(); + UserName = userName; + RemoteAddress = remoteAddress; + } + + public override string Pack() { + return string.Format("2\t{0}\t-1\t0\fipaddr\f{1}\f{2}\t{3}\t10010", Timestamp, UserName, RemoteAddress, SequenceId); + } + } +} diff --git a/SharpChat/SockChatServer.cs b/SharpChat/SockChatServer.cs index df5b5cc..02ee95f 100644 --- a/SharpChat/SockChatServer.cs +++ b/SharpChat/SockChatServer.cs @@ -99,7 +99,7 @@ namespace SharpChat { new PardonUserCommand(msz), new PardonAddressCommand(msz), new BanListCommand(msz), - new RemoteAddressCommand(), + new WhoisCommand(), }); ushort port = config.SafeReadValue("port", DEFAULT_PORT); @@ -184,7 +184,7 @@ namespace SharpChat { if(banUser is not null) { if(banDuration == TimeSpan.MinValue) { - Context.SendTo(conn.User, new LegacyCommandResponse(LCR.FLOOD_WARN, false)); + Context.SendTo(conn.User, new FloodWarningPacket()); } else { Context.BanUser(conn.User, banDuration, UserDisconnectReason.Flood);