SecondBot/CLI/HttpServer/SecondBotHttpServer.cs
2020-05-15 23:24:17 +01:00

277 lines
11 KiB
C#

using BetterSecondBot.HttpWebUi;
using BetterSecondBotShared.IO;
using BetterSecondBotShared.Json;
using BetterSecondBotShared.logs;
using BetterSecondBotShared.Static;
using BSB;
using BSB.bottypes;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
namespace BetterSecondBot.HttpServer
{
public class json_http_reply
{
public bool status;
public string message;
public string redirect;
}
public class SecondBotHttpServer
{
protected HttpListener listener;
protected SecondBot Bot;
public SecondBot GetBot { get { return Bot; } }
protected JsonConfig Config;
public JsonConfig GetConfig { get { return Config; } }
protected HTTPCommandsInterface post_controler;
protected string url = "";
public bool ShutdownHTTP { get; set; }
public bool HTTPCnCmode { get; set; }
public string GetStatus()
{
string reply = "";
if(Bot == null)
{
reply += "- No bot";
}
else
{
Bot.LastStatusMessage = Bot.GetStatus();
reply += Bot.LastStatusMessage;
}
if(listener == null)
{
reply += " - No HTTP listener";
}
return reply;
}
public void NewBot()
{
Bot = new SecondBot();
}
public void KillBot()
{
Bot.GetClient.Network.Logout();
Bot.GetClient.Network.Shutdown(OpenMetaverse.NetworkManager.DisconnectType.ClientInitiated);
Bot = null;
}
public void StartHttpServer(JsonConfig setConfig)
{
StartHttpServer(setConfig, null);
}
public void StartHttpServer(SecondBot LinkBot, JsonConfig setConfig)
{
StartHttpServer(setConfig, LinkBot);
}
protected void StartHttpServer(JsonConfig setConfig, SecondBot LinkBot)
{
Config = setConfig;
Bot = LinkBot;
int port = Math.Abs(Config.Http_Port);
if (Config.Http_Host == "docker")
{
url = "http://*:" + port.ToString() + "/";
}
else
{
if (url.StartsWith("http://") == false)
{
url = "http://localhost:" + port.ToString() + "";
}
if (url.EndsWith("/") == false)
{
url = "" + url + "/";
}
}
OpenHttpService();
}
protected async Task HandleIncomingConnections()
{
webui myUI = new webui(Config,Bot);
while (Bot.KillMe == false)
{
HttpListenerContext ctx = await listener.GetContextAsync().ConfigureAwait(true);
HttpListenerRequest req = ctx.Request;
HttpListenerResponse resp = ctx.Response;
resp.Cookies = req.Cookies;
string test = req.Url.AbsoluteUri;
if (helpers.notempty(Config.Http_PublicUrl) == true)
{
// normal mapped
test = test.Replace(Config.Http_PublicUrl, "");
//rproxy weirdness
string use_http_url = Config.Http_PublicUrl.Replace("http://", "");
use_http_url = Config.Http_PublicUrl.Replace("https://", "");
test = test.Replace(":" + Config.Http_Port.ToString(), "");
test = test.Replace(use_http_url, "");
test = test.Replace("https://", "");
test = test.Replace("http://", "");
}
json_http_reply reply = new json_http_reply();
List<string> http_args = test.Split('/', StringSplitOptions.RemoveEmptyEntries).ToList();
byte[] data;
resp.StatusCode = 200;
resp.ContentType = "text/html";
if (req.HttpMethod == "GET")
{
KeyValuePair<string, byte[]> replydata = myUI.Get_Process(req, resp, http_args);
if (replydata.Value != null)
{
resp.ContentType = replydata.Key;
data = replydata.Value;
resp.ContentLength64 = data.LongLength;
await resp.OutputStream.WriteAsync(data, 0, data.Length).ConfigureAwait(true);
resp.Close();
}
else
{
data = Encoding.UTF8.GetBytes("error with service");
resp.ContentLength64 = data.LongLength;
resp.ContentType = "text/html";
await resp.OutputStream.WriteAsync(data, 0, data.Length).ConfigureAwait(true);
resp.Close();
}
}
else if (req.HttpMethod == "POST")
{
string text;
using (var reader = new StreamReader(req.InputStream, req.ContentEncoding))
{
text = reader.ReadToEnd();
}
//"command = logout & args = &passkey = Kp83VZaEhcy"
string[] pairs = text.Split('&', StringSplitOptions.RemoveEmptyEntries);
Dictionary<string, string> post_args = new Dictionary<string, string>();
foreach (string B in pairs)
{
string[] bits = B.Split('=', StringSplitOptions.RemoveEmptyEntries);
if (bits.Length == 2)
{
post_args.Add(bits[0].Trim(), HttpUtility.UrlDecode(bits[1].Trim()));
}
}
resp.StatusCode = 200;
if (myUI.Post_process(reply, req, resp, http_args, post_args) == true)
{
bool accept_request = false;
string why_request_failed = "Bad signing request";
string command = "";
string arg = "";
string signing_code;
if (post_args.ContainsKey("command") == true)
{
command = post_args["command"];
if (post_args.ContainsKey("args") == true)
{
arg = post_args["args"];
}
if (post_args.ContainsKey("code") == true)
{
signing_code = post_args["code"];
string raw = "" + command + "" + arg + "" + Config.Security_SignedCommandkey + "";
string hashcheck = helpers.GetSHA1(raw);
if (hashcheck == signing_code)
{
accept_request = true;
}
}
else
{
why_request_failed = "Post missing arg value";
}
}
else
{
why_request_failed = "Post missing command value";
}
reply.message = why_request_failed;
reply.status = accept_request;
if (accept_request == true)
{
reply.message = String.Join("###", post_controler.Call(command, arg));
}
}
if (reply.redirect != "")
{
if (reply.redirect != null)
{
if (reply.redirect == "/")
{
reply.redirect = Config.Http_PublicUrl;
}
else
{
reply.redirect = Config.Http_PublicUrl + reply.redirect;
}
}
}
data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(reply));
resp.ContentLength64 = data.LongLength;
await resp.OutputStream.WriteAsync(data, 0, data.Length).ConfigureAwait(true);
resp.Close();
}
}
}
protected void OpenHttpService()
{
post_controler = new HTTPCommandsInterface(Bot,this);
Thread t = new Thread(delegate (object _)
{
bool ok = true;
try
{
listener = new HttpListener();
listener.Prefixes.Add(url);
listener.Start();
}
catch (Exception e)
{
ok = false;
ConsoleLog.Crit("Unable to setup http service: "+e.Message+"");
ConsoleLog.Crit("if running on windows please check: https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/configuring-http-and-https?redirectedfrom=MSDN");
}
if (ok == true)
{
bool clean_exit = false;
ConsoleLog.Status("Listening for connections on " + url + "");
while (clean_exit == false)
{
try
{
ConsoleLog.Status("HTTP ready");
Task listenTask = HandleIncomingConnections();
listenTask.GetAwaiter().GetResult();
}
catch (Exception e)
{
ConsoleLog.Status("HTTP died");
//ConsoleLog.Status("Http interface killed itself: " + e.ToString() + "");
}
}
}
listener.Close();
})
{
Name = "http thead",
IsBackground = true
};
t.Start();
}
}
}