diff --git a/Assembly/ASMInfo.cs b/Assembly/ASMInfo.cs index 5388c57..7c30401 100644 --- a/Assembly/ASMInfo.cs +++ b/Assembly/ASMInfo.cs @@ -6,7 +6,7 @@ using System.Reflection; [assembly: AssemblyCompany("ZNI")] [assembly: AssemblyAlgorithmId(System.Configuration.Assemblies.AssemblyHashAlgorithm.MD5)] [assembly: AssemblyCopyright("© 2020 Tara Piccari")] -[assembly: AssemblyFileVersion("5.405")] +[assembly: AssemblyFileVersion("5.513")] [assembly: AssemblyDescription("Second Life Bot - BotCore5")] @@ -15,7 +15,7 @@ namespace Bot.Assemble public class ASMInfo { public static string BotName = "ZBotCore"; - public static double BotVer = 5.405; + public static double BotVer = 5.513; public static string GitPassword { get diff --git a/ConfigSystem/MainConfiguration.cs b/ConfigSystem/MainConfiguration.cs index 8307f27..5921b18 100644 --- a/ConfigSystem/MainConfiguration.cs +++ b/ConfigSystem/MainConfiguration.cs @@ -55,6 +55,8 @@ namespace Bot public string GitPassword { get; set; } = "NOT_SET"; + public int WebServerPort { get; set; } = 35591; + public Dictionary BotAdmins { get; set; } = new Dictionary(); diff --git a/WebHookServer/HookCmds.cs b/WebHookServer/HookCmds.cs new file mode 100644 index 0000000..54eb3a0 --- /dev/null +++ b/WebHookServer/HookCmds.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; + +namespace Bot.WebHookServer +{ + class HookCmds + { + + public HttpListener listener; + public void OnWebHook(IAsyncResult ar) + { + HttpListenerContext CTX = null; + try + { + CTX = listener.EndGetContext(ar); + } + catch (Exception e) + { + BotSession.Instance.Logger.info(log: "ERROR: Getting the end context for the listener failed"); + return; + } + listener.BeginGetContext(OnWebHook, null); + + + Stream body = CTX.Request.InputStream; + StreamReader SR = new StreamReader(body, CTX.Request.ContentEncoding); + string Response = SR.ReadToEnd(); + + if (!Directory.Exists("request_log")) Directory.CreateDirectory("request_log"); + + + string RequestPath = CTX.Request.RawUrl; + if (RequestPath.EndsWith("/")) RequestPath = RequestPath.Substring(0, RequestPath.Length - 1); + + string CustomReplyStr = ""; + + WebhookRegistry.HTTPResponseData reply = WebhookRegistry.Instance.RunCommand(RequestPath, Response, CTX.Request.Headers, CTX.Request.HttpMethod); + + + CustomReplyStr = reply.ReplyString; + byte[] buffer = Encoding.UTF8.GetBytes("\n" + CustomReplyStr); + CTX.Response.ContentLength64 = buffer.Length; + CTX.Response.AddHeader("Server", "1.6"); + CTX.Response.StatusCode = reply.Status; + if (reply.ReturnContentType != "" && reply.ReturnContentType != null) + { + CTX.Response.ContentType = reply.ReturnContentType; + } + Stream output = CTX.Response.OutputStream; + output.Write(buffer, 0, buffer.Length); + output.Close(); + + } + } +} diff --git a/WebHookServer/WebHookServer.cs b/WebHookServer/WebHookServer.cs new file mode 100644 index 0000000..e6e6999 --- /dev/null +++ b/WebHookServer/WebHookServer.cs @@ -0,0 +1,76 @@ +/* + +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the GPLv2 + +*/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Bot; +using Bot.CommandSystem; +using OpenMetaverse; +using System.IO; +using System.Net.Http; +using System.Net; +using Newtonsoft.Json; + +namespace Bot.WebHookServer +{ + class GitServer : IProgram + { + public HttpListener listener; + public MessageHandler.MessageHandleEvent MHEx; + public string ProgramName + { + get { return "GitServer"; } + } + + public float ProgramVersion + { + get { return 1.6f; } + } + + public string getTick() + { + + return ""; + } + + public void passArguments(string data) + { + // dont throw, just silently do nothing + } + + public void LoadConfiguration() + { + + } + + public void onIMEvent(object sender, InstantMessageEventArgs e) + { + } + public void run(GridClient client, MessageHandler MH, CommandRegistry registry) + { + try + { + listener = new HttpListener(); + MHEx = MH.callbacks; + listener.Prefixes.Add($"https://*:{MainConfiguration.Instance.WebServerPort}/"); + + listener.Start(); + var hc = new HookCmds(); + hc.listener = listener; + + listener.BeginGetContext(hc.OnWebHook, null); + + }catch(Exception e) + { + BotSession.Instance.MHE(MessageHandler.Destinations.DEST_LOCAL, UUID.Zero, "Error: Program could not escalate to Admin Privileges. WebHook engine not running"); + } + } + } +} diff --git a/WebHookServer/WebhookAttribs.cs b/WebHookServer/WebhookAttribs.cs new file mode 100644 index 0000000..3250c03 --- /dev/null +++ b/WebHookServer/WebhookAttribs.cs @@ -0,0 +1,25 @@ +/* + +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the GPLv2 + +*/ + +using System; +using System.Reflection; + +namespace Bot.WebHookServer +{ + + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public class WebhookAttribs : Attribute + { + public string Path = ""; + public MethodInfo AssignedMethod = null; + public string HTTPMethod = "GET"; + public WebhookAttribs(string WebPath) + { + Path = WebPath; + } + } +} diff --git a/WebHookServer/WebhookRegistry.cs b/WebHookServer/WebhookRegistry.cs new file mode 100644 index 0000000..d674042 --- /dev/null +++ b/WebHookServer/WebhookRegistry.cs @@ -0,0 +1,203 @@ +/* + +Copyright © 2019 Tara Piccari (Aria; Tashia Redrose) +Licensed under the GPLv2 + +*/ + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Reflection; +using System.Text; +using System.IO; +using Newtonsoft.Json; + +namespace Bot.WebHookServer +{ + public sealed class WebhookRegistry + { + private static WebhookRegistry _reg = null; + private static readonly object locks = new object(); + static WebhookRegistry() + { + + } + public static WebhookRegistry Instance + { + get + { + lock (locks) + { + if(_reg == null) + { + _reg = new WebhookRegistry(); + _reg.LocateHooks(); + } + return _reg; + } + } + } + + + public Dictionary hooks = new Dictionary(); + + public void LocateHooks() + { + try + { + int i = 0; + for(i = 0; i< AppDomain.CurrentDomain.GetAssemblies().Length; i++) + { + // Grab Assembly + Assembly asm = null; + try + { + asm = AppDomain.CurrentDomain.GetAssemblies()[i]; + }catch(Exception e) + { + + } + + if(asm != null) + { + int ii = 0; + for(ii = 0; ii arguments = new List(); + string sCheck = zAPIPath.Path; + bool Found = true; // Default to true + if (method != zAPIPath.HTTPMethod) Found = false; + + string[] aCheck = sCheck.Split(new[] { '/' }); + string[] actualRequest = path.Split(new[] { '/', '?' }); // if it contains a ?, we'll put that into the GETBody + string theArgs = ""; + + if (path.Contains('?')) + { + // continue + string[] tmp1 = path.Split(new[] { '?' }); + theArgs = tmp1[1]; + actualRequest = tmp1[0].Split(new[] { '/' }); + + } + if (actualRequest.Length == aCheck.Length) + { + + int i = 0; + + for (i = 0; i < aCheck.Length; i++) + { + // TODO: CHANGE THIS SLOPPY MESS TO REGEX.. FOR NOW IT WORKS! + if (aCheck[i] == "%") + { + arguments.Add(actualRequest[i]); + } + else + { + + if (aCheck[i] == actualRequest[i]) + { + // we're good! + + } + else + { + // check other path hooks before returning 404! + Found = false; + } + } + } + } + else Found = false; + + arguments.Add(theArgs); + + if (Found) + { + // Run the method + Console.WriteLine("Running: " + zAPIPath.Path + "; " + zAPIPath.AssignedMethod.Name + "; For inbound: " + path); + object _method = Activator.CreateInstance(zAPIPath.AssignedMethod.DeclaringType); + hrd = (HTTPResponseData)zAPIPath.AssignedMethod.Invoke(_method, new object[] { arguments, body, method, headers }); + +// Console.WriteLine("====> " + hrd.ReplyString); + + return hrd; + } + } + // an API Path wasn't found + // check the filesystem + string[] noArgPath = path.Split(new[] { '?' }); + if (File.Exists($"htdocs/{noArgPath[0]}")) // This will provide a way to display HTML to the user. If the server must process data internally, please use a method & attribute. Nothing is stopping you from also loading in a HTML/js file and returning a stylized response. + { + hrd.Status = 200; + hrd.ReplyString = File.ReadAllText($"htdocs/{noArgPath[0]}"); + Dictionary customHeaders = null; // This is mainly going to be used in instances where the domain-server needs a document but CORS isnt set + + + + } + return hrd; + } + + public struct HTTPResponseData + { + public int Status; + public string ReplyString; + public string ReturnContentType; + } + } +}