diff --git a/Core/bottypes/CommandsBot.cs b/Core/bottypes/CommandsBot.cs index 31702d2..882ea4a 100644 --- a/Core/bottypes/CommandsBot.cs +++ b/Core/bottypes/CommandsBot.cs @@ -6,13 +6,13 @@ using OpenMetaverse; using OpenMetaverse.Assets; using BetterSecondBotShared.Static; using BetterSecondBotShared.logs; -using BetterSecondBot.HttpService; -using Core.Static; -using System.Reflection; -using Newtonsoft.Json; -using System.Net.Http; -using BetterSecondBotShared.Json; - +using BetterSecondBot.HttpService; +using Core.Static; +using System.Reflection; +using Newtonsoft.Json; +using System.Net.Http; +using BetterSecondBotShared.Json; + namespace BetterSecondBot.bottypes { public abstract class CommandsBot : StorageBot @@ -130,37 +130,37 @@ namespace BetterSecondBot.bottypes public override void AfterBotLoginHandler() { - if (reconnect == false) - { - loadCommands(); - LoadCustomCommands(); + if (reconnect == false) + { + loadCommands(); + LoadCustomCommands(); } Client.Self.RequestBalance(); base.AfterBotLoginHandler(); } - public string[] getFullListOfCommands() - { - List output = new List(); - foreach(string A in endpointcommandmap.Keys) - { - output.Add(A.ToLowerInvariant()); - } - return output.ToArray(); + public string[] getFullListOfCommands() + { + List output = new List(); + foreach(string A in endpointcommandmap.Keys) + { + output.Add(A.ToLowerInvariant()); + } + return output.ToArray(); } - public string[] GetFullListOfCommandsWithCustoms() - { - List output = new List(); - foreach (string A in endpointcommandmap.Keys) - { - output.Add(A.ToLowerInvariant()); - } - foreach(string A in custom_commands.Keys) - { - output.Add(A.ToLowerInvariant()); - } - return output.ToArray(); + public string[] GetFullListOfCommandsWithCustoms() + { + List output = new List(); + foreach (string A in endpointcommandmap.Keys) + { + output.Add(A.ToLowerInvariant()); + } + foreach(string A in custom_commands.Keys) + { + output.Add(A.ToLowerInvariant()); + } + return output.ToArray(); } public void NotecardAddContent(string target_notecard_storage_id, string content) @@ -259,37 +259,37 @@ namespace BetterSecondBot.bottypes } - protected void JsonChatControler(string message, string sender_name, UUID sender_uuid, bool avatar, bool group, UUID group_uuid, bool localchat, bool fromme) - { - if((group == true) || (localchat == true) || (fromme == true)) - { - return; - } - try - { - bool frommaster = true; - if (sender_uuid != master_uuid) - { - frommaster = false; - } - if (frommaster == true) - { - if (message == "fakerestart") - { - AlertMessageEventArgs Alertargs = new AlertMessageEventArgs("this is a fake restart message",new Random().Next(99999).ToString(), new OpenMetaverse.StructuredData.OSDMap()); - AlertEvent(this, Alertargs); - return; - } - - } - JsonApiDefine APIE = JsonConvert.DeserializeObject(message); - - CoreCommandLib(sender_uuid, frommaster, APIE.cmd, APIE.args, APIE.signing, APIE.reply); - } - catch (Exception e) - { - LogFormater.Crit("[CoreCommandLib] exploded: " + e.Message + ""); - } + protected void JsonChatControler(string message, string sender_name, UUID sender_uuid, bool avatar, bool group, UUID group_uuid, bool localchat, bool fromme) + { + if((group == true) || (localchat == true) || (fromme == true)) + { + return; + } + try + { + bool frommaster = true; + if (sender_uuid != master_uuid) + { + frommaster = false; + } + if (frommaster == true) + { + if (message == "fakerestart") + { + AlertMessageEventArgs Alertargs = new AlertMessageEventArgs("this is a fake restart message",new Random().Next(99999).ToString(), new OpenMetaverse.StructuredData.OSDMap()); + AlertEvent(this, Alertargs); + return; + } + + } + JsonApiDefine APIE = JsonConvert.DeserializeObject(message); + + CoreCommandLib(sender_uuid, frommaster, APIE.cmd, APIE.args, APIE.signing, APIE.reply); + } + catch (Exception e) + { + LogFormater.Crit("[CoreCommandLib] exploded: " + e.Message + ""); + } } @@ -301,231 +301,182 @@ namespace BetterSecondBot.bottypes protected override void BotChatControler(string message, string sender_name, UUID sender_uuid, bool avatar, bool group, UUID group_uuid, bool localchat, bool fromme) { - base.BotChatControler(message, sender_name, sender_uuid, avatar, group, group_uuid, localchat, fromme); - if ((group == true) || (localchat == true) || (fromme == true)) - { - return; - } - try - { - JsonApiDefine APIE = JsonConvert.DeserializeObject(message); - JsonChatControler(message, sender_name, sender_uuid, avatar, group, group_uuid, localchat, fromme); - } - catch - { - // Old style [non json] to json converter [to be phased out] - string signing_code = ""; - string[] S1 = message.Split(new[] { "@@@" }, StringSplitOptions.RemoveEmptyEntries); - if (S1.Length == 2) - { - signing_code = S1[1]; - } - S1 = S1[0].Split(new[] { "#|#" }, StringSplitOptions.RemoveEmptyEntries); - string outputto = "none"; - if (S1.Length == 2) - { - outputto = S1[1]; - } - S1 = S1[0].Split(new[] { "|||" }, StringSplitOptions.RemoveEmptyEntries); - string[] args = new string[] { }; - if (S1.Length == 2) - { - args = S1[1].Split(new[] { "~#~" }, StringSplitOptions.RemoveEmptyEntries); - } - JsonApiDefine Apihandoff = new JsonApiDefine(); - Apihandoff.cmd = S1[0]; - Apihandoff.args = args; - Apihandoff.reply = outputto; - Apihandoff.signing = signing_code; - message = JsonConvert.SerializeObject(Apihandoff); - JsonChatControler(message, sender_name, sender_uuid, avatar, group, group_uuid, localchat, fromme); - } - } - - protected KeyValuePair callAPIcommand(string command, string[] args) - { - command = commandnameLowerToReal[command.ToLowerInvariant()]; - try - { - string ott = Tokens.OneTimeToken(); - WebApiControllerWithTokens Endpoint = commandEndpoints[endpointcommandmap[command]]; - MethodInfo theMethod = Endpoint.GetType().GetMethod(command); - if (theMethod != null) - { - List argsList = args.ToList(); - argsList.Add(ott); - string reply = "Inconnect number of args expected: " + theMethod.GetParameters().Count().ToString() + " but got: " + argsList.Count.ToString(); - bool status = false; - if (argsList.Count == theMethod.GetParameters().Count()) - { - status = true; - try - { - if (Endpoint == null) - { - return new KeyValuePair(false, "Endpoint is null"); - } - if (argsList.Count == 0) - { - return new KeyValuePair(false, "Zero args at final check require at min 1"); - } - object[] argsWorker = argsList.ToArray(); - object processed = theMethod.Invoke(Endpoint, argsWorker); - reply = "Error"; - if(processed != null) - { - reply = JsonConvert.SerializeObject(processed); - } - return new KeyValuePair(status, reply); - } - catch (Exception e) - { - return new KeyValuePair(false, e.Message); - } - } - return new KeyValuePair(status, reply); - } - else - { - return new KeyValuePair(false, "theMethod is null"); - } - } - catch (Exception e) - { - return new KeyValuePair(false, e.Message); - } - } - - protected void CoreCommandLib(UUID sender, bool Master, string command, string[] args, string signingcode, string targetreply) - { - bool accepted = Master; - if(accepted == false) - { - string raw = "" + command + "" + string.Join("~#~", args) + "" + myconfig.Security_SignedCommandkey + ""; - string hashcheck = helpers.GetSHA1(raw); - if (hashcheck == signingcode) - { - accepted = true; - } - } - if (accepted == true) - { - CallAPI(command, args, targetreply); - } - } - - public string CallAPI(string command, string[] args) - { - return CallAPI(command, args, "None"); - } - public string CallAPI(string command, string[] args, string replyvia) - { - return CallAPI(command, args, replyvia, false); - } - - protected string customCommand(string command, string[] args, string replyvia, bool customcommand) - { - if (customcommand == true) - { - SmartCommandReply(false, replyvia, "Custom command lockout", command); - return "Custom command lockout"; - } - int commands_issued = 0; - foreach (string A in custom_commands[command]) - { - List command_args_split = A.Split(new[] { "|||" }, StringSplitOptions.RemoveEmptyEntries).ToList(); - int loop = 1; - string args_passed = ""; - if (command_args_split.Count() == 2) - { - args_passed = command_args_split[1]; - while ((loop <= 5) && (loop <= args.Length)) - { - args_passed = args_passed.Replace("[C_ARG_" + loop.ToString() + "]", args[loop - 1]); - loop++; - } - } - CallAPI(command_args_split[0], args_passed.Split("~#~"), "None", true); - commands_issued++; - } - return "Issued "+ commands_issued.ToString()+" commands"; - } - public string CallAPI(string command, string[] args, string replyvia, bool customcommand) - { - if (command == null) - { - return "No command"; - } - if (replyvia == null) - { - replyvia = "none"; - } - KeyValuePair statusreply = new KeyValuePair(false, "Not processed"); - if (commandnameLowerToReal.ContainsKey(command.ToLowerInvariant()) == false) - { - if (custom_commands.ContainsKey(command) == false) - { - SmartCommandReply(false, replyvia, "Unknown command", command); - return "{ status: \"false\", message: \"Unknown\" }"; - } - statusreply = new KeyValuePair(true, customCommand(command, args, replyvia, customcommand)); - } - else - { - statusreply = callAPIcommand(command, args); - } - SmartCommandReply(statusreply.Key, replyvia, statusreply.Value, command); - return "{ status: \"" + statusreply.Key.ToString() + "\", message: \""+ statusreply.Value+"\" command: \""+command+"\", args: \""+string.Join("~#~",args)+"\" }"; - } - - - - - - - - protected void loadCommands() - { - commandsLoaded = true; - Dictionary commandmodules = http_commands_helper.getCommandModules(); - foreach (Type entry in commandmodules.Values) - { - LoadCommandEndpoint(entry); - } + base.BotChatControler(message, sender_name, sender_uuid, avatar, group, group_uuid, localchat, fromme); + if ((group == true) || (localchat == true) || (fromme == true)) + { + return; + } + try + { + JsonApiDefine APIE = JsonConvert.DeserializeObject(message); + JsonChatControler(message, sender_name, sender_uuid, avatar, group, group_uuid, localchat, fromme); + } + catch + { + // Old style [non json] to json converter [to be phased out] + string signing_code = ""; + string[] S1 = message.Split(new[] { "@@@" }, StringSplitOptions.RemoveEmptyEntries); + if (S1.Length == 2) + { + signing_code = S1[1]; + } + S1 = S1[0].Split(new[] { "#|#" }, StringSplitOptions.RemoveEmptyEntries); + string outputto = "none"; + if (S1.Length == 2) + { + outputto = S1[1]; + } + S1 = S1[0].Split(new[] { "|||" }, StringSplitOptions.RemoveEmptyEntries); + string[] args = new string[] { }; + if (S1.Length == 2) + { + args = S1[1].Split(new[] { "~#~" }, StringSplitOptions.RemoveEmptyEntries); + } + JsonApiDefine Apihandoff = new JsonApiDefine(); + Apihandoff.cmd = S1[0]; + Apihandoff.args = args; + Apihandoff.reply = outputto; + Apihandoff.signing = signing_code; + message = JsonConvert.SerializeObject(Apihandoff); + JsonChatControler(message, sender_name, sender_uuid, avatar, group, group_uuid, localchat, fromme); + } } - protected virtual void LoadCommandEndpoint(Type endpointtype) - { - WebApiControllerWithTokens controler = (WebApiControllerWithTokens)Activator.CreateInstance(endpointtype, args: new object[] { this, Tokens }); - controler.disableIPlockout(); - commandEndpoints.Add(endpointtype.Name, controler); - foreach (MethodInfo M in endpointtype.GetMethods()) - { - bool isCallable = false; - foreach (CustomAttributeData At in M.CustomAttributes) - { - if (At.AttributeType.Name == "RouteAttribute") - { - isCallable = true; - break; - } - } - if (isCallable == true) - { - if (endpointcommandmap.ContainsKey(M.Name) == true) - { - Warn("Namespace: "+endpointtype.Name+" / Command: " + M.Name + " already found in " + endpointcommandmap[M.Name]); - continue; - } - if(commandnameLowerToReal.ContainsKey(M.Name.ToLowerInvariant()) == true) - { - Warn("Namespace: " + endpointtype.Name + " / Command: " + M.Name + " already found in " + endpointcommandmap[M.Name]); - continue; - } - endpointcommandmap.Add(M.Name, endpointtype.Name); - commandnameLowerToReal.Add(M.Name.ToLowerInvariant(), M.Name); - } - } + protected KeyValuePair callAPIcommand(string command, string[] args) + { + + } + + protected void CoreCommandLib(UUID sender, bool Master, string command, string[] args, string signingcode, string targetreply) + { + bool accepted = Master; + if(accepted == false) + { + string raw = "" + command + "" + string.Join("~#~", args) + "" + myconfig.Security_SignedCommandkey + ""; + string hashcheck = helpers.GetSHA1(raw); + if (hashcheck == signingcode) + { + accepted = true; + } + } + if (accepted == true) + { + CallAPI(command, args, targetreply); + } + } + + public string CallAPI(string command, string[] args) + { + return CallAPI(command, args, "None"); + } + public string CallAPI(string command, string[] args, string replyvia) + { + return CallAPI(command, args, replyvia, false); + } + + protected string customCommand(string command, string[] args, string replyvia, bool customcommand) + { + if (customcommand == true) + { + SmartCommandReply(false, replyvia, "Custom command lockout", command); + return "Custom command lockout"; + } + int commands_issued = 0; + foreach (string A in custom_commands[command]) + { + List command_args_split = A.Split(new[] { "|||" }, StringSplitOptions.RemoveEmptyEntries).ToList(); + int loop = 1; + string args_passed = ""; + if (command_args_split.Count() == 2) + { + args_passed = command_args_split[1]; + while ((loop <= 5) && (loop <= args.Length)) + { + args_passed = args_passed.Replace("[C_ARG_" + loop.ToString() + "]", args[loop - 1]); + loop++; + } + } + CallAPI(command_args_split[0], args_passed.Split("~#~"), "None", true); + commands_issued++; + } + return "Issued "+ commands_issued.ToString()+" commands"; + } + public string CallAPI(string command, string[] args, string replyvia, bool customcommand) + { + if (command == null) + { + return "No command"; + } + if (replyvia == null) + { + replyvia = "none"; + } + KeyValuePair statusreply = new KeyValuePair(false, "Not processed"); + if (commandnameLowerToReal.ContainsKey(command.ToLowerInvariant()) == false) + { + if (custom_commands.ContainsKey(command) == false) + { + SmartCommandReply(false, replyvia, "Unknown command", command); + return "{ status: \"false\", message: \"Unknown\" }"; + } + statusreply = new KeyValuePair(true, customCommand(command, args, replyvia, customcommand)); + } + else + { + statusreply = callAPIcommand(command, args); + } + SmartCommandReply(statusreply.Key, replyvia, statusreply.Value, command); + return "{ status: \"" + statusreply.Key.ToString() + "\", message: \""+ statusreply.Value+"\" command: \""+command+"\", args: \""+string.Join("~#~",args)+"\" }"; + } + + + + + + + + protected void loadCommands() + { + commandsLoaded = true; + Dictionary commandmodules = http_commands_helper.getCommandModules(); + foreach (Type entry in commandmodules.Values) + { + LoadCommandEndpoint(entry); + } + } + + protected virtual void LoadCommandEndpoint(Type endpointtype) + { + WebApiControllerWithTokens controler = (WebApiControllerWithTokens)Activator.CreateInstance(endpointtype, args: new object[] { this, Tokens }); + controler.disableIPlockout(); + commandEndpoints.Add(endpointtype.Name, controler); + foreach (MethodInfo M in endpointtype.GetMethods()) + { + bool isCallable = false; + foreach (CustomAttributeData At in M.CustomAttributes) + { + if (At.AttributeType.Name == "RouteAttribute") + { + isCallable = true; + break; + } + } + if (isCallable == true) + { + if (endpointcommandmap.ContainsKey(M.Name) == true) + { + Warn("Namespace: "+endpointtype.Name+" / Command: " + M.Name + " already found in " + endpointcommandmap[M.Name]); + continue; + } + if(commandnameLowerToReal.ContainsKey(M.Name.ToLowerInvariant()) == true) + { + Warn("Namespace: " + endpointtype.Name + " / Command: " + M.Name + " already found in " + endpointcommandmap[M.Name]); + continue; + } + endpointcommandmap.Add(M.Name, endpointtype.Name); + commandnameLowerToReal.Add(M.Name.ToLowerInvariant(), M.Name); + } + } } } } diff --git a/SecondBotEvents/Services/Commands.cs b/SecondBotEvents/Services/Commands.cs index e551113..be79f00 100644 --- a/SecondBotEvents/Services/Commands.cs +++ b/SecondBotEvents/Services/Commands.cs @@ -1,8 +1,13 @@ -using OpenMetaverse; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using OpenMetaverse; using SecondBotEvents.Config; using System; using System.Collections.Generic; using System.Text; +using System.Linq; +using System.Reflection; +using System.Net.Http; namespace SecondBotEvents.Services { @@ -13,6 +18,50 @@ namespace SecondBotEvents.Services public CommandsService(EventsSecondBot setMaster) : base(setMaster) { myConfig = new CommandsConfig(master.fromEnv, master.fromFolder); + loadCommands(); + } + + protected void loadCommands() + { + Dictionary commandmodules = http_commands_helper.getCommandModules(); + foreach (Type entry in commandmodules.Values) + { + LoadCommandEndpoint(entry); + } + } + + protected virtual void LoadCommandEndpoint(Type endpointtype) + { + CommandsAPI controler = (CommandsAPI)Activator.CreateInstance(endpointtype, args: new object[] { master }); + controler.disableIPlockout(); + commandEndpoints.Add(endpointtype.Name, controler); + foreach (MethodInfo M in endpointtype.GetMethods()) + { + bool isCallable = false; + foreach (CustomAttributeData At in M.CustomAttributes) + { + if (At.AttributeType.Name == "RouteAttribute") + { + isCallable = true; + break; + } + } + if (isCallable == true) + { + if (endpointcommandmap.ContainsKey(M.Name) == true) + { + LogFormater.Warn("Namespace: " + endpointtype.Name + " / Command: " + M.Name + " already found in " + endpointcommandmap[M.Name]); + continue; + } + if (commandnameLowerToReal.ContainsKey(M.Name.ToLowerInvariant()) == true) + { + LogFormater.Warn("Namespace: " + endpointtype.Name + " / Command: " + M.Name + " already found in " + endpointcommandmap[M.Name]); + continue; + } + endpointcommandmap.Add(M.Name, endpointtype.Name); + commandnameLowerToReal.Add(M.Name.ToLowerInvariant(), M.Name); + } + } } Dictionary acceptTokens = new Dictionary(); @@ -68,20 +117,6 @@ namespace SecondBotEvents.Services acceptNewCommands = false; } - protected void BotClientRestart(object o, BotClientNotice e) - { - acceptNewCommands = false; - Console.WriteLine("Commands service [Attached to new client]"); - master.botClient.client.Network.LoggedOut += BotLoggedOut; - master.botClient.client.Network.SimConnected += BotLoggedIn; - } - - protected void BotLoggedOut(object o, LoggedOutEventArgs e) - { - master.botClient.client.Network.SimConnected += BotLoggedIn; - Console.WriteLine("Commands service [Waiting for connect]"); - } - protected void BotImMessage(object o, InstantMessageEventArgs e) { if(acceptNewCommands == false) @@ -96,51 +131,187 @@ namespace SecondBotEvents.Services { return; } - bool acceptMessage = true; - bool requireSigning = false; + bool acceptMessage = false; + bool requireSigning = true; switch (e.IM.Dialog) { case InstantMessageDialog.MessageFromObject: { - requireSigning = true; + acceptMessage = true; break; } case InstantMessageDialog.MessageFromAgent: // shared with SessionSend case InstantMessageDialog.SessionSend: { - if(e.IM.GroupIM == false) + if(e.IM.GroupIM == true) // groups are unable to issue commands { - acceptMessage = false; break; } if (myConfig.GetOnlySelectedAvs() == false) { - acceptMessage = false; break; } + requireSigning = false; acceptMessage = myConfig.GetAvsCSV().Contains(e.IM.FromAgentName); break; } default: { - acceptMessage = false; break; } } - if (requireSigning == true) - { - acceptMessage = acceptMessageSigned(e.IM.Message); - } - if (acceptMessage == false) + if(acceptMessage == false) { return; } - + SignedCommand C = new SignedCommand(e.IM.Message, + requireSigning, + myConfig.GetEnforceTimeWindow(), + myConfig.GetTimeWindowSecs(), + myConfig.GetSharedSecret() + ); + if (C.accepted == false) + { + return; + } + KeyValuePair reply = runCommand(C); + if(C.replyTarget != null) + { + SmartCommandReply(reply.Key, C.replyTarget, reply.Value, C.command); + } } - protected bool acceptMessageSigned(string message) + protected void SmartCommandReply(bool run_status, string target, string output, string command) { - return false; + string mode = "CHAT"; + UUID target_avatar = UUID.Zero; + int target_channel = 0; + if (target.StartsWith("http://")) + { + mode = "HTTP"; + } + else if (UUID.TryParse(target, out target_avatar) == true) + { + mode = "IM"; + } + else + { + if (int.TryParse(target, out target_channel) == false) + { + mode = "None"; + } + } + if (mode == "CHAT") + { + if (target_channel <= 0) + { + LogFormater.Crit("[SmartReply] output Channel must be zero or higher - using 99"); + target_channel = 999; + } + master.botClient.client.Self.Chat(output, target_channel, ChatType.Normal); + } + else if (mode == "IM") + { + master.botClient.client.Self.InstantMessage(target_avatar, output); + } + else if (mode == "HTTP") + { + Dictionary values = new Dictionary + { + { "reply", output }, + { "command", command }, + }; + + var content = new FormUrlEncodedContent(values); + try + { + HTTPclient.PostAsync(target, content); + } + catch (Exception e) + { + LogFormater.Crit("[SmartReply] HTTP failed: " + e.Message + ""); + } + } + } + protected HttpClient HTTPclient = new HttpClient(); + + public KeyValuePair runCommand(SignedCommand C) + { + C.command = commandnameLowerToReal[C.command.ToLowerInvariant()]; + try + { + string ott = SingleUseToken(); + CommandsAPI Endpoint = commandEndpoints[endpointcommandmap[C.command]]; + MethodInfo theMethod = Endpoint.GetType().GetMethod(C.command); + if (theMethod != null) + { + List argsList = C.args.ToList(); + argsList.Add(ott); + string reply = "Inconnect number of args expected: " + theMethod.GetParameters().Count().ToString() + " but got: " + argsList.Count.ToString(); + bool status = false; + if (argsList.Count == theMethod.GetParameters().Count()) + { + status = true; + try + { + if (Endpoint == null) + { + return new KeyValuePair(false, "Endpoint is null"); + } + if (argsList.Count == 0) + { + return new KeyValuePair(false, "Zero args at final check require at min 1"); + } + object[] argsWorker = argsList.ToArray(); + object processed = theMethod.Invoke(Endpoint, argsWorker); + reply = "Error"; + if (processed != null) + { + reply = JsonConvert.SerializeObject(processed); + } + return new KeyValuePair(status, reply); + } + catch (Exception e) + { + return new KeyValuePair(false, e.Message); + } + } + return new KeyValuePair(status, reply); + } + return new KeyValuePair(false, "theMethod is null"); + } + catch (Exception e) + { + return new KeyValuePair(false, e.Message); + } + } + + public string[] getFullListOfCommands() + { + List output = new List(); + foreach (string A in endpointcommandmap.Keys) + { + output.Add(A.ToLowerInvariant()); + } + return output.ToArray(); + } + + protected Dictionary commandEndpoints = new Dictionary(); + protected Dictionary endpointcommandmap = new Dictionary(); // command = endpoint + protected Dictionary commandnameLowerToReal = new Dictionary(); + + protected void BotClientRestart(object o, BotClientNotice e) + { + acceptNewCommands = false; + Console.WriteLine("Commands service [Attached to new client]"); + master.botClient.client.Network.LoggedOut += BotLoggedOut; + master.botClient.client.Network.SimConnected += BotLoggedIn; + } + + protected void BotLoggedOut(object o, LoggedOutEventArgs e) + { + master.botClient.client.Network.SimConnected += BotLoggedIn; + Console.WriteLine("Commands service [Waiting for connect]"); } protected void BotLoggedIn(object o, SimConnectedEventArgs e) @@ -151,4 +322,118 @@ namespace SecondBotEvents.Services Console.WriteLine("Commands service [accepting IM commands]"); } } + + public class SignedCommand + { + public string command; + public string signingCode; + public string[] args; + public string replyTarget; + public bool accepted = false; + public long unixtimeOfCommand = 0; + public SignedCommand(string input, bool requireSigning, bool requireTimewindow, int windowSize, string secret) + { + unpackInput(input); + if (requireSigning == false) + { + accepted = true; // just accept the command + return; + } + long dif = SecondbotHelpers.UnixTimeNow() - unixtimeOfCommand; + if ((requireTimewindow == true) && ((unixtimeOfCommand == 0) || (dif > windowSize))) // bad time window + { + return; + } + if ((signingCode == null) ) // no signing code + { + return; + } + string raw = command; + raw = raw + string.Join("~#~", args); + if (requireTimewindow == true) + { + raw = raw + unixtimeOfCommand.ToString(); + } + raw = raw + secret; + string cooked = SecondbotHelpers.GetSHA1(raw); + if (cooked != signingCode) + { + return; // invaild signing code + } + accepted = true; + + + } + protected void unpackInput(string input) + { + if (IsValidJson(input) == false) + { + unpackStringToClass(input); + return; + } + unpackJsonToClass(); + } + protected JToken obj = null; + protected void unpackStringToClass(string input) + { + // command|||args~#~args~#~args#|#reply_target@@@sha1 [inc time if required];|;unixtime of command + + string[] bits = input.Split(";|;"); + if(bits.Length == 2) + { + unixtimeOfCommand = Convert.ToInt32(bits[1]); + } + bits = bits[0].Split("#|#"); + if(bits.Length == 2) + { + replyTarget = bits[1]; + } + bits = bits[0].Split("|||"); + command = bits[0]; + args = new string[] { }; + if(bits.Length == 2) + { + args = bits[1].Split("~#~"); + } + } + protected void unpackJsonToClass() + { + command = jsonHelper.GetValue(obj, "cmd", null); + args = jsonHelper.GetValue(obj, "args", null); + signingCode = jsonHelper.GetValue(obj, "signing", null); + replyTarget = jsonHelper.GetValue(obj, "reply", null); + unixtimeOfCommand = jsonHelper.GetValue(obj, "unixtime", 0); + } + + private bool IsValidJson(string strInput) + { + if (string.IsNullOrWhiteSpace(strInput)) { return false; } + strInput = strInput.Trim(); + if ((strInput.StartsWith("{") && strInput.EndsWith("}")) || //For object + (strInput.StartsWith("[") && strInput.EndsWith("]"))) //For array + { + try + { + obj = JToken.Parse(strInput); + return true; + } + catch + { + return false; + } + } + return false; + } + } + + public static class jsonHelper + { + public static T GetValue(this JToken jToken, string key, T defaultValue = default(T)) + { + dynamic ret = jToken[key]; + if (ret == null) return defaultValue; + if (ret is JObject) return JsonConvert.DeserializeObject(ret.ToString()); + return (T)ret; + } + } }