diff --git a/Content.Server/Administration/Commands/BanCommand.cs b/Content.Server/Administration/Commands/BanCommand.cs index 45040a5b21..5a5ad641c9 100644 --- a/Content.Server/Administration/Commands/BanCommand.cs +++ b/Content.Server/Administration/Commands/BanCommand.cs @@ -125,7 +125,7 @@ public sealed class BanCommand : LocalizedCommands Rid = EntitySystem.Get().RoundId, BanId = banId }; - _pandaWeb.SendBotMessage(utkaBanned); + _pandaWeb.SendBotPostMessage(utkaBanned); _entMan.EventBus.RaiseEvent(EventSource.Local, utkaBanned); //WD end } diff --git a/Content.Server/Administration/Managers/BanManager.cs b/Content.Server/Administration/Managers/BanManager.cs index 17f965ac2a..a152984a91 100644 --- a/Content.Server/Administration/Managers/BanManager.cs +++ b/Content.Server/Administration/Managers/BanManager.cs @@ -529,7 +529,7 @@ public sealed class BanManager : IBanManager, IPostInjectInit BanId = banId }; - _pandaWeb.SendBotMessage(utkaBanned); + _pandaWeb.SendBotPostMessage(utkaBanned); _entMan.EventBus.RaiseEvent(EventSource.Local, utkaBanned); } diff --git a/Content.Server/Administration/Systems/BwoinkSystem.cs b/Content.Server/Administration/Systems/BwoinkSystem.cs index 028c267c5e..17131117ab 100644 --- a/Content.Server/Administration/Systems/BwoinkSystem.cs +++ b/Content.Server/Administration/Systems/BwoinkSystem.cs @@ -658,7 +658,7 @@ namespace Content.Server.Administration.Systems Entity = entity }; - _pandaWeb.SendBotMessage(utkaAhelpEvent); + _pandaWeb.SendBotPostMessage(utkaAhelpEvent); } //WD-EDIT } diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs index 10f41aad79..0c3f5d830d 100644 --- a/Content.Server/Chat/Managers/ChatManager.cs +++ b/Content.Server/Chat/Managers/ChatManager.cs @@ -331,14 +331,14 @@ namespace Content.Server.Chat.Managers _adminLogger.Add(LogType.Chat, LogImpact.Low, $"OOC from {player:Player}: {message}"); //WD-EDIT - var toUtkaMessage = new UtkaChatEventMessage() + var toUtkaMessage = new UtkaChatMessageEvent() { Command = "ooc", Ckey = player.Name, Message = message, }; - _pandaWeb.SendBotMessage(toUtkaMessage); + _pandaWeb.SendBotPostMessage(toUtkaMessage); //WD-EDIT } @@ -374,14 +374,14 @@ namespace Content.Server.Chat.Managers _adminLogger.Add(LogType.Chat, $"Admin chat from {player:Player}: {message}"); //WD-EDIT - var asayEventMessage = new UtkaChatEventMessage() + var asayEventMessage = new UtkaChatMessageEvent() { Command = "asay", Ckey = player.Name, Message = message }; - _pandaWeb.SendBotMessage(asayEventMessage); + _pandaWeb.SendBotPostMessage(asayEventMessage); //WD-EDIT } diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index d2cf68e1ed..86427ff6cd 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -648,7 +648,7 @@ public sealed partial class ChatSystem : SharedChatSystem CharacterName = MetaData(source).EntityName }; - _pandaWeb.SendBotMessage(utkaEmoteEvent); + _pandaWeb.SendBotPostMessage(utkaEmoteEvent); //WD-EDIT } diff --git a/Content.Server/GameTicking/GameTicker.RoundFlow.cs b/Content.Server/GameTicking/GameTicker.RoundFlow.cs index d759f2ee43..e18eba56c4 100644 --- a/Content.Server/GameTicking/GameTicker.RoundFlow.cs +++ b/Content.Server/GameTicking/GameTicker.RoundFlow.cs @@ -567,7 +567,7 @@ namespace Content.Server.GameTicking Message = status }; - _pandaWeb.SendBotMessage(utkaRoundStatusEvent); + _pandaWeb.SendBotPostMessage(utkaRoundStatusEvent); } diff --git a/Content.Server/RoundEnd/RoundEndSystem.cs b/Content.Server/RoundEnd/RoundEndSystem.cs index 6798f2eae0..acc54f377f 100644 --- a/Content.Server/RoundEnd/RoundEndSystem.cs +++ b/Content.Server/RoundEnd/RoundEndSystem.cs @@ -307,7 +307,7 @@ namespace Content.Server.RoundEnd Message = status }; - _pandaWeb.SendBotMessage(utkaRoundStatusEvent); + _pandaWeb.SendBotPostMessage(utkaRoundStatusEvent); } //WD-EDIT diff --git a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs index de569955e0..a4a5d2f1e2 100644 --- a/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs +++ b/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs @@ -584,6 +584,6 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem Message = status }; - _pandaWeb.SendBotMessage(utkaRoundStatusEvent); + _pandaWeb.SendBotPostMessage(utkaRoundStatusEvent); } } diff --git a/Content.Server/White/PandaSocket/Commands/PandaBanCommand.cs b/Content.Server/White/PandaSocket/Commands/PandaBanCommand.cs index 2bfe08c7db..a3418199f9 100644 --- a/Content.Server/White/PandaSocket/Commands/PandaBanCommand.cs +++ b/Content.Server/White/PandaSocket/Commands/PandaBanCommand.cs @@ -131,7 +131,7 @@ public sealed class PandaBanCommand : IPandaCommand BanId = banId }; - _pandaWeb.SendBotMessage(utkaBanned); + _pandaWeb.SendBotPostMessage(utkaBanned); _entMan.EventBus.RaiseEvent(EventSource.Local, utkaBanned); } diff --git a/Content.Server/White/PandaSocket/Interfaces/IPandaStatusHandlerContext.cs b/Content.Server/White/PandaSocket/Interfaces/IPandaStatusHandlerContext.cs index 7e478b64bf..d5d7d9c1b1 100644 --- a/Content.Server/White/PandaSocket/Interfaces/IPandaStatusHandlerContext.cs +++ b/Content.Server/White/PandaSocket/Interfaces/IPandaStatusHandlerContext.cs @@ -17,6 +17,7 @@ public interface IPandaStatusHandlerContext Stream RequestBody { get; } Uri Url { get; } bool IsGetLike { get; } + bool IsPostLike { get; } IReadOnlyDictionary RequestHeaders { get; } IDictionary ResponseHeaders { get; } diff --git a/Content.Server/White/PandaSocket/Main/PandaCommunication.cs b/Content.Server/White/PandaSocket/Main/PandaCommunication.cs index e0472ab617..ff1dbe6836 100644 --- a/Content.Server/White/PandaSocket/Main/PandaCommunication.cs +++ b/Content.Server/White/PandaSocket/Main/PandaCommunication.cs @@ -8,7 +8,13 @@ public class PandaBaseMessage public virtual string? Command { get; set; } } -public class UtkaOOCRequest : PandaBaseMessage +public class PandaBaseRequestEventMessage : PandaBaseMessage +{ + [JsonPropertyName("token")] + public string? Token { get; set; } +} + +public class UtkaOOCRequest : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "ooc"; @@ -20,7 +26,7 @@ public class UtkaOOCRequest : PandaBaseMessage public string? Message { get; set; } } -public class UtkaAsayRequest : PandaBaseMessage +public class UtkaAsayRequest : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "asay"; @@ -32,7 +38,7 @@ public class UtkaAsayRequest : PandaBaseMessage public string? Message { get; set; } } -public class UtkaPmRequest : PandaBaseMessage +public class UtkaPmRequest : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "discordpm"; @@ -56,7 +62,7 @@ public class UtkaPmResponse : PandaBaseMessage public bool? Message { get; set; } } -public class UtkaWhoRequest : PandaBaseMessage +public class UtkaWhoRequest : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "who"; @@ -71,7 +77,7 @@ public class UtkaWhoResponse : PandaBaseMessage public List? Players { get; set; } } -public class UtkaAdminWhoRequest : PandaBaseMessage +public class UtkaAdminWhoRequest : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "adminwho"; @@ -86,7 +92,7 @@ public class UtkaAdminWhoResponse : PandaBaseMessage public List? Admins { get; set; } } -public class UtkaStatusRequest : PandaBaseMessage +public class UtkaStatusRequest : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "status"; @@ -116,7 +122,7 @@ public class UtkaStatusResponse : PandaBaseMessage public string? StationCode { get; set; } } -public sealed class UtkaBanRequest : PandaBaseMessage +public sealed class UtkaBanRequest : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "ban"; @@ -146,7 +152,7 @@ public sealed class UtkaBanResponse : PandaBaseMessage public bool? Banned { get; set; } } -public sealed class UtkaJobBanRequest : PandaBaseMessage +public sealed class UtkaJobBanRequest : PandaBaseRequestEventMessage { public override string? Command => "jobban"; @@ -178,7 +184,7 @@ public sealed class UtkaJobBanResponse : PandaBaseMessage public bool? Banned { get; set; } } -public sealed class UtkaRestartRoundRequest : PandaBaseMessage +public sealed class UtkaRestartRoundRequest : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "restartround"; @@ -193,7 +199,7 @@ public sealed class UtkaRestartRoundResponse : PandaBaseMessage public bool? Restarted { get; set; } } -public sealed class UtkaUnbanRequest : PandaBaseMessage +public sealed class UtkaUnbanRequest : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "unban"; @@ -214,7 +220,7 @@ public sealed class UtkaUnbanResponse : PandaBaseMessage public bool? Unbanned { get; set; } } -public sealed class UtkaUnJobBanRequest : PandaBaseMessage +public sealed class UtkaUnJobBanRequest : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "unjobban"; @@ -235,7 +241,7 @@ public sealed class UtkaUnJobBanResponse : PandaBaseMessage public bool? Unbanned { get; set; } } -public sealed class UtkaBannedEvent : PandaBaseMessage +public sealed class UtkaBannedEvent : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "banned"; @@ -265,7 +271,7 @@ public sealed class UtkaBannedEvent : PandaBaseMessage public int? BanId { get; set; } } -public sealed class UtkaChatEventMessage : PandaBaseMessage +public sealed class UtkaChatMessageEvent : PandaBaseRequestEventMessage { [JsonPropertyName("ckey")] public string? Ckey { get; set; } @@ -274,7 +280,7 @@ public sealed class UtkaChatEventMessage : PandaBaseMessage public string? Message { get; set; } } -public sealed class UtkaRoundStatusEvent : PandaBaseMessage +public sealed class UtkaRoundStatusEvent : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "roundstatus"; @@ -283,7 +289,7 @@ public sealed class UtkaRoundStatusEvent : PandaBaseMessage public string? Message { get; set; } } -public sealed class UtkaChatMeEvent : PandaBaseMessage +public sealed class UtkaChatMeEvent : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "me"; @@ -298,7 +304,7 @@ public sealed class UtkaChatMeEvent : PandaBaseMessage public string? CharacterName { get; set; } } -public sealed class UtkaAhelpPmEvent : PandaBaseMessage +public sealed class UtkaAhelpPmEvent : PandaBaseRequestEventMessage { [JsonPropertyName("command")] public override string? Command => "pm"; diff --git a/Content.Server/White/PandaSocket/Main/PandaStatusHost.Handlers.cs b/Content.Server/White/PandaSocket/Main/PandaStatusHost.Handlers.cs index bafa651c74..ab5cd3dec6 100644 --- a/Content.Server/White/PandaSocket/Main/PandaStatusHost.Handlers.cs +++ b/Content.Server/White/PandaSocket/Main/PandaStatusHost.Handlers.cs @@ -44,12 +44,12 @@ public sealed partial class PandaStatusHost private async Task HandleRequest(IPandaStatusHandlerContext context) { - if (!context.IsGetLike || context.Url!.AbsolutePath != "/request") + if (!context.IsPostLike || context.Url!.AbsolutePath != "/request") { return false; } - if (!ValidateMessage(context.Url.Query, out var message) || message == null) + if (!ValidatePostMessage(context.RequestBody, out var message) || message == null) return false; ExecuteCommand(context, message); diff --git a/Content.Server/White/PandaSocket/Main/PandaStatusHost.cs b/Content.Server/White/PandaSocket/Main/PandaStatusHost.cs index 1fe8ba2858..f2b8c7580d 100644 --- a/Content.Server/White/PandaSocket/Main/PandaStatusHost.cs +++ b/Content.Server/White/PandaSocket/Main/PandaStatusHost.cs @@ -162,9 +162,51 @@ public sealed partial class PandaStatusHost : IDisposable return false; var collection = HttpUtility.ParseQueryString(message); -#pragma warning disable CS8714 var json = JsonSerializer.Serialize(collection.AllKeys.ToDictionary(y => y!, y => collection[y])); -#pragma warning restore CS8714 + var jsonDocument = JsonDocument.Parse(json); + var root = jsonDocument.RootElement; + + if (!root.TryGetProperty("token", out var token)) + return false; + + if (token.GetString() != _token) + return false; + + if (!root.TryGetProperty("command", out var commandNameElement)) + return false; + + var commandName = commandNameElement.GetString(); + if (commandName == null) + return false; + + var pandaCommand = Commands.Values.FirstOrDefault(x => x.Name == commandName); + if (pandaCommand == null) + return false; + + var messageType = pandaCommand.RequestMessageType; + + try + { + baseMessage = JsonConvert.DeserializeObject(json, messageType) as PandaBaseMessage; + } + catch (Exception e) + { + return false; + } + + return true; + } + + private bool ValidatePostMessage(Stream message, out PandaBaseMessage? baseMessage) + { + baseMessage = null; + + var reader = new StreamReader(message, Encoding.UTF8); + + var task = Task.Run(async () => await reader.ReadToEndAsync()); + _taskManager.BlockWaitOnTask(task); + var json = task.GetAwaiter().GetResult(); + var jsonDocument = JsonDocument.Parse(json); var root = jsonDocument.RootElement; @@ -219,6 +261,7 @@ public sealed partial class PandaStatusHost : IDisposable public Stream RequestBody => _context.Request.InputStream; public Uri Url => _context.Request.Url!; public bool IsGetLike => RequestMethod == HttpMethod.Head || RequestMethod == HttpMethod.Get; + public bool IsPostLike => RequestMethod == HttpMethod.Post; public IReadOnlyDictionary RequestHeaders { get; } public bool KeepAlive diff --git a/Content.Server/White/PandaSocket/Main/PandaWebManager.cs b/Content.Server/White/PandaSocket/Main/PandaWebManager.cs index b3f045bf5e..68118af193 100644 --- a/Content.Server/White/PandaSocket/Main/PandaWebManager.cs +++ b/Content.Server/White/PandaSocket/Main/PandaWebManager.cs @@ -1,5 +1,6 @@ using System.Linq; using System.Net.Http; +using System.Text; using System.Web; using Content.Shared.White; using Newtonsoft.Json; @@ -28,7 +29,7 @@ public sealed class PandaWebManager _cfg.OnValueChanged(WhiteCVars.UtkaClientBind, uri => _utkaUri = uri, true); } - public async void SendBotMessage(PandaBaseMessage message) + public async void SendBotGetMessage(PandaBaseMessage message) { if (_utkaUri is null || _token is null) return; @@ -50,4 +51,26 @@ public sealed class PandaWebManager return; } } + + public async void SendBotPostMessage(PandaBaseRequestEventMessage message) + { + if (_utkaUri is null || _token is null) + return; + + message.Token = _token; + + var request = $"http://{_utkaUri}"; + var json = JsonSerializer.Serialize(message, message.GetType()); + + HttpContent content = new StringContent(json, Encoding.UTF8, "application/json"); + + try + { + await _httpClient.PostAsync(request, content); + } + catch (Exception e) + { + return; + } + } }