ForgeCore/WebHookServer/WebhookRegistry.cs

203 lines
7.1 KiB
C#

/*
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<string, WebhookAttribs> hooks = new Dictionary<string, WebhookAttribs>();
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<asm.GetTypes().Length; ii++)
{
Type T = null;
try
{
T = asm.GetTypes()[ii];
}catch(Exception e)
{
}
if(T != null)
{
// Grab the WebHook Attribute
if (T.IsClass)
{
foreach(MethodInfo mi in T.GetMethods())
{
WebhookAttribs[] wha = (WebhookAttribs[])mi.GetCustomAttributes(typeof(WebhookAttribs), false);
//
int ix = 0;
for(ix=0;ix<wha.Length;ix++)
{
WebhookAttribs attribu = wha[ix];
attribu.AssignedMethod = mi;
hooks.Add(attribu.Path, attribu);
}
}
}
}
}
}
}
}catch(Exception e)
{
}
}
public HTTPResponseData RunCommand(string path, string body, NameValueCollection headers, string method)
{
// Run the command then return the response string from the server
HTTPResponseData NotFound = new HTTPResponseData();
NotFound.ReplyString = "More water is required!";
NotFound.Status = 418;
//HTTPResponseData hrd = (HTTPResponseData)fnc.Invoke(obj, new object[] { body, headers });
//
HTTPResponseData hrd = NotFound;
foreach (WebhookAttribs zAPIPath in hooks.Values)
{
// compare strings; If a % symbol is located, then skip that so long as the inbound string matches totally.
// Append the value of % in the inbound request to the array passed to the function
List<string> arguments = new List<string>();
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<string, string> 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;
}
}
}