Probably should have included the files ;)

This commit is contained in:
RaidMax 2015-08-21 20:14:02 -05:00
parent 7af9c31ea0
commit 7ca0b654ac
4 changed files with 866 additions and 0 deletions

View File

@ -0,0 +1,565 @@
using System;
using System.Collections.Generic;
using SharedLibrary;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.Collections.Specialized;
namespace Webfront_Plugin
{
class Framework
{
private List<Server> activeServers;
public Framework()
{
activeServers = new List<Server>();
}
public void addServer(Server S)
{
activeServers.Add(S);
}
public void removeServer(Server S)
{
if (activeServers.Contains(S))
activeServers.Remove(S);
}
private String processTemplate(String Input, String Param)
{
try
{
Server requestedServer = null;
int requestPageNum = 0;
int ID = 0;
String Query = "";
if (Param != null)
{
NameValueCollection querySet = System.Web.HttpUtility.ParseQueryString(Param);
if (querySet["server"] != null)
requestedServer = activeServers.Find(x => x.pID() == Int32.Parse(querySet["server"]));
if (querySet["page"] != null)
requestPageNum = Int32.Parse(querySet["page"]);
if (querySet["id"] != null)
ID = Int32.Parse(querySet["id"]);
if (querySet["query"] != null)
Query = querySet["query"];
}
String Pattern = @"\{\{.+\}\}";
Regex Search = new Regex(Pattern, RegexOptions.IgnoreCase);
MatchCollection Matches = Search.Matches(Input);
foreach (Match match in Matches)
{
Input = processReplacements(Input, match.Value, requestPageNum, ID, Query, requestedServer);
}
return Input;
}
catch (Exception E)
{
Page Error = new error();
return Error.Load().Replace("{{ERROR}}", E.Message);
}
}
private String parsePagination(int totalItems, int itemsPerPage, int currentPage, String Page)
{
StringBuilder output = new StringBuilder();
output.Append("<div id=pages>");
if (currentPage > 0)
output.AppendFormat("<a href=/{0}?page={1}>PREV</a>", Page, currentPage - 1);
double totalPages = Math.Ceiling(((float)totalItems / itemsPerPage));
output.Append("<span id=pagination>" + (currentPage + 1) + "/" + totalPages + "</span>");
if ((currentPage + 1) < totalPages)
output.AppendFormat("<a href=/{0}?page={1}>NEXT</a>", Page, currentPage + 1);
output.Append("</div>");
return output.ToString();
}
private String processReplacements(String Input, String Macro, int curPage, int ID, String Query, params Server[] Servers)
{
if (Macro.Length < 5)
return "";
String Looking = Macro.Substring(2, Macro.Length - 4);
if (Looking == "SERVERS")
{
int cycleFix = 0;
StringBuilder buffer = new StringBuilder();
foreach (Server S in activeServers)
{
StringBuilder players = new StringBuilder();
if (S.getClientNum() < 1)
players.Append("<h2>No Players</h2>");
else
{
int count = 0;
double currentPlayers = S.statusPlayers.Count;
foreach (Player P in S.getPlayers())
{
if (P == null)
continue;
if (count % 2 == 0)
{
switch (cycleFix)
{
case 0:
players.Append("<tr class='row-grey'>");
cycleFix = 1;
break;
case 1:
players.Append("<tr class='row-white'>");
cycleFix = 0;
break;
}
}
players.AppendFormat("<td><a href='/player?id={0}'>{1}</a></td>", P.databaseID, SharedLibrary.Utilities.nameHTMLFormatted(P));
if (count % 2 != 0)
{
players.Append("</tr>");
}
count++;
}
}
buffer.AppendFormat(@"<table cellpadding=0 cellspacing=0 class=server>
<tr>
<th class=server_title><span>{0}</span></th>
<th class=server_map><span>{1}</span></th>
<th class=server_players><span>{2}</span></th>
<th class=server_gametype><span>{3}</span></th>
<th><a href=/stats>Stats</a></th>
<th><a href=/bans>Bans</a></th>
<th><a class='history' href='/graph?server={4}'>History</a></th>
</tr>
</table>
<table cellpadding='0' cellspacing='0' class='players'>
{5}
</table>",
S.getName(), S.getMap(), S.getClientNum() + "/" + S.getMaxClients(), SharedLibrary.Utilities.gametypeLocalized(S.getGametype()), S.pID(), players.ToString());
buffer.AppendFormat("<div class='chatHistory' id='chatHistory_{0}'></div><script type='text/javascript'>$( document ).ready(function() {{ setInterval({1}loadChatMessages({0}, '#chatHistory_{0}'){1}, 2500); }});</script><div class='null' style='clear:both;'></div>", S.pID(), '\"');
//if (S.getClientNum() > 0)
// buffer.AppendFormat("<form class='chatOutFormat' action={1}javascript:chatRequest({0}, 'chatEntry_{0}'){1}><input class='chatFormat_text' type='text' placeholder='Enter a message...' id='chatEntry_{0}'/><input class='chatFormat_submit' type='submit'/></form>", S.pID(), '\"');
buffer.Append("<hr/>");
}
return Input.Replace(Macro, buffer.ToString());
}
if(Looking == "CHAT")
{
StringBuilder chatMessages = new StringBuilder();
chatMessages.Append("<table id='table_chatHistory'>");
if (Servers.Length > 0 && Servers[0] != null)
{
foreach (Chat Message in Servers[0].chatHistory)
chatMessages.AppendFormat("<tr><td class='chat_name' style='text-align: left;'>{0}</td><td class='chat_message'>{1}</td><td class='chat_time' style='text-align: right;'>{2}</td></tr>", SharedLibrary.Utilities.nameHTMLFormatted(Message.Origin), Message.Message, Message.timeString());
}
chatMessages.Append("</table>");
return chatMessages.ToString();
}
if (Looking == "PLAYER")
{
StringBuilder buffer = new StringBuilder();
Server S = activeServers[0];
buffer.Append("<table class='player_info'><tr><th>Name</th><th>Aliases</th><th>IP</th><th>Rating</th><th>Level</th><th>Connections</th><th>Last Seen</th><th>Profile</th>");
List<Player> matchingPlayers = new List<Player>();
if (ID > 0)
matchingPlayers.Add(S.clientDB.getPlayer(ID));
else if (Query.Length > 2)
{
matchingPlayers = S.clientDB.findPlayers(Query);
if (matchingPlayers == null)
matchingPlayers = new List<Player>();
List<int> matchedDatabaseIDs = new List<int>();
foreach (Aliases matchingAlias in S.aliasDB.findPlayers(Query))
matchedDatabaseIDs.Add(matchingAlias.Number);
foreach (Player matchingP in S.clientDB.getPlayers(matchedDatabaseIDs))
{
if (matchingPlayers.Find(x => x.databaseID == matchingP.databaseID) == null)
matchingPlayers.Add(matchingP);
}
}
if (matchingPlayers == null)
buffer.Append("</table>");
else
{
foreach (Player Player in matchingPlayers)
{
if (Player == null)
continue;
buffer.Append("<tr>");
StringBuilder str = new StringBuilder();
List<Aliases> allAlliases = S.getAliases(Player);
List<String> nameAlias = new List<String>();
foreach (Aliases A in allAlliases)
{
foreach (String Name in A.Names.Distinct())
nameAlias.Add(Name);
}
foreach (String Name in nameAlias.Distinct())
str.AppendFormat("<span>{0}</span><br/>", Utilities.stripColors(Name));
StringBuilder IPs = new StringBuilder();
if (false)
{
/*foreach (Player a in aliases)
{
foreach (String ip in a.Alias.IPS)
{
if (!IPs.ToString().Contains(ip))
IPs.AppendFormat("<span>{0}</span><br/>", ip);
}
}*/
}
else
IPs.Append("Hidden");
Int64 forumID = 0;
if (Player.npID.Length == 16)
{
forumID = Int64.Parse(Player.npID.Substring(0, 16), System.Globalization.NumberStyles.AllowHexSpecifier);
forumID = forumID - 76561197960265728;
}
String Screenshot = String.Empty;
//if (logged)
Screenshot = String.Format("<a href='http://server.nbsclan.org/screen.php?id={0}&name={1}'><div style='background-image:url(http://server.nbsclan.org/shutter.png); width: 20px; height: 20px;float: right; position:relative; right: 21%; background-size: contain;'></div></a>", forumID, Player.Name);
buffer.AppendFormat("<td><a style='float: left;' href='{9}'>{0}</a>{10}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td><td>{5}</td><td>{6} ago</td><td><a href='https://repziw4.de/memberlist.php?mode=viewprofile&u={7}'>{8}</a></td>", Player.Name, str, IPs, 0, SharedLibrary.Utilities.levelHTMLFormatted(Player.Level), Player.Connections, Player.getLastConnection(), forumID, Player.Name, "/player?id=" + Player.databaseID, Screenshot);
buffer.Append("</tr>");
}
buffer.Append("</table>");
return Input.Replace(Macro, buffer.ToString());
}
}
if (Looking == "BANS")
{
StringBuilder buffer = new StringBuilder();
Server S = activeServers[0];
buffer.Append("<table cellspacing=0 class=bans>");
int limitPerPage = 30;
int Pagination = curPage;
int totalBans = S.Bans.Count;
int range;
int start = Pagination * limitPerPage;
int cycleFix = 0;
if (totalBans <= limitPerPage)
range = totalBans - 1;
else if ((totalBans - start) < limitPerPage)
range = (totalBans - start);
else
range = limitPerPage;
List<Ban> Bans = new List<Ban>();
if (totalBans > 0)
Bans = S.Bans.GetRange(start, range).OrderByDescending(x => x.When).ToList();
if (Bans.Count == 0)
buffer.Append("<span style='font-size: 16pt;'>No bans yet.</span>");
else
{
buffer.Append("<h1 style=margin-top: 0;>{{TIME}}</h1><hr /><tr><th>Name</th><th style=text-align:left;>Offense</th><th style=text-align:left;>Banned By</th><th style='width: 175px; text-align:right;padding-right: 80px;'>Time</th></tr>");
if (Bans[0] != null)
buffer = buffer.Replace("{{TIME}}", "From " + SharedLibrary.Utilities.timePassed(Bans[0].When) + " ago" + " &mdash; " + totalBans + " total");
List<String> npIDs = new List<string>();
foreach (Ban B in Bans)
npIDs.Add(B.npID);
Player[] bannedPlayers = S.clientDB.getPlayers(npIDs).ToArray();
for (int i = 0; i < Bans.Count-1; i++)
{
if (Bans[i] == null)
continue;
Player P = bannedPlayers[i];
Player B;
if (Bans[i].bannedByID == Bans[i].npID)
B = new Player("IW4MAdmin", "", 0, SharedLibrary.Player.Permission.Banned, 0, "", 0, "");
else
B = S.clientDB.getPlayer(Bans[i].bannedByID, -1);
if (P == null)
P = new Player("Unknown", "n/a", 0, 0, 0, "Unknown", 0, "");
if (B == null)
B = new Player("Unknown", "n/a", 0, 0, 0, "Unknown", 0, "");
if (P.lastOffense == String.Empty)
P.lastOffense = "Evade";
if (P != null && B != null)
{
String Prefix;
if (cycleFix % 2 == 0)
Prefix = "class=row-grey";
else
Prefix = "class=row-white";
String Link = "/player?id=" + P.databaseID;
buffer.AppendFormat("<tr {4}><td><a href='{5}'>{0}</a></th><td style='border-left: 3px solid #bbb; text-align:left;'>{1}</td><td style='border-left: 3px solid #bbb;text-align:left;'>{2}</td><td style='width: 175px; text-align:right;'>{3}</td></tr></div>", P.Name, P.lastOffense, SharedLibrary.Utilities.nameHTMLFormatted(B), Bans[i].getWhen(), Prefix, Link);
cycleFix++;
}
}
}
buffer.Append("</table><hr/>");
if (totalBans > limitPerPage)
buffer.Append(parsePagination(totalBans, limitPerPage, Pagination, "bans"));
return Input.Replace(Macro, buffer.ToString());
}
if (Looking == "TITLE")
return Input.Replace(Macro, "IW4MAdmin by RaidMax");
if (Looking == "VERSION")
return Input.Replace(Macro, "0.9.5");
return "PLACEHOLDER";
}
public String processRequest(Kayak.Http.HttpRequestHead request)
{
Page requestedPage = new notfound();
Page Header = new header();
Page Footer = new footer();
if (request.Path == "/")
requestedPage = new main();
else
{
string p = request.Path.ToLower().Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)[0];
switch (p)
{
case "bans":
requestedPage = new bans();
break;
case "player":
requestedPage = new player();
break;
case "graph":
requestedPage = new graph();
break;
case "stats":
requestedPage = new stats();
break;
case "chat":
requestedPage = new chat();
return processTemplate(requestedPage.Load(), request.QueryString);
case "error":
requestedPage = new error();
break;
default:
requestedPage = new notfound();
break;
}
}
return processTemplate(Header.Load(), null) + processTemplate(requestedPage.Load(), request.QueryString) + processTemplate(Footer.Load(), null);
}
}
abstract class Page
{
public abstract String Load();
public abstract String Name { get; }
protected String loadHTML()
{
IFile HTML = new IFile("webfront\\" + this.Name + ".html");
String Contents = HTML.getLines();
HTML.Close();
return Contents;
}
}
class notfound : Page
{
public override String Name
{
get { return "notfound"; }
}
public override String Load()
{
return loadHTML();
}
}
class main : Page
{
public override String Name
{
get { return "main"; }
}
public override String Load()
{
return loadHTML();
}
}
class bans : Page
{
public override String Name
{
get { return "bans"; }
}
public override String Load()
{
return loadHTML();
}
}
class header : Page
{
public override String Name
{
get { return "header"; }
}
public override String Load()
{
return loadHTML();
}
}
class footer : Page
{
public override String Name
{
get { return "footer"; }
}
public override String Load()
{
return loadHTML();
}
}
class player : Page
{
public override String Name
{
get { return "player"; }
}
public override String Load()
{
return loadHTML();
}
}
class stats : Page
{
public override String Name
{
get { return "stats"; }
}
public override String Load()
{
return loadHTML();
}
}
class graph : Page
{
public override String Name
{
get { return "graph"; }
}
public override String Load()
{
return loadHTML();
}
}
class chat : Page
{
public override String Name
{
get { return "chat"; }
}
public override String Load()
{
return "{{CHAT}}";
}
}
class error : Page
{
public override String Name
{
get { return "error"; }
}
public override String Load()
{
return loadHTML();
}
}
}

37
Webfront Plugin/Main.cs Normal file
View File

@ -0,0 +1,37 @@
using System;
using SharedLibrary;
using System.Threading;
namespace Webfront_Plugin
{
public class Webfront : Notify
{
private static Manager webManager;
public override void onEvent(Event E)
{
if (webManager != null)
{
if (E.Type == Event.GType.Start)
{
Manager.webFront.addServer(E.Owner);
E.Owner.Log.Write("Webfront now has access to server on port " + E.Owner.getPort(), Log.Level.Production);
}
if (E.Type == Event.GType.Stop)
{
Manager.webFront.removeServer(E.Owner);
E.Owner.Log.Write("Webfront has lost access to server on port " + E.Owner.getPort(), Log.Level.Production);
}
}
}
public override void onLoad()
{
webManager = new Manager();
Thread webManagerThread = new Thread(new ThreadStart(webManager.Init));
webManagerThread.Name = "Webfront";
webManagerThread.Start();
}
}
}

200
Webfront Plugin/Manager.cs Normal file
View File

@ -0,0 +1,200 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kayak;
using Kayak.Http;
using System.Net;
namespace Webfront_Plugin
{
class Manager
{
public IScheduler webScheduler { get; private set; }
public static Framework webFront { get; private set; }
public Manager()
{
}
public void Init()
{
webScheduler = KayakScheduler.Factory.Create(new SchedulerDelegate());
var server = KayakServer.Factory.CreateHttp(new RequestDelegate(), webScheduler);
webFront = new Framework();
using (server.Listen(new IPEndPoint(IPAddress.Any, 1624)))
webScheduler.Start();
}
}
class SchedulerDelegate : ISchedulerDelegate
{
public void OnException(IScheduler scheduler, Exception e)
{
}
public void OnStop(IScheduler scheduler)
{
}
}
class RequestDelegate : IHttpRequestDelegate
{
public void OnRequest(HttpRequestHead request, IDataProducer requestBody, IHttpResponseDelegate response)
{
/*if (request.Method.ToUpperInvariant() == "POST" && request.Uri.StartsWith("/bufferedecho"))
{
// when you subecribe to the request body before calling OnResponse,
// the server will automatically send 100-continue if the client is
// expecting it.
requestBody.Connect(new BufferedConsumer(bufferedBody =>
{
var headers = new HttpResponseHead()
{
Status = "200 OK",
Headers = new Dictionary<string, string>()
{
{ "Content-Type", "text/plain" },
{ "Content-Length", request.Headers["Content-Length"] },
{ "Connection", "close" }
}
};
response.OnResponse(headers, new BufferedProducer(bufferedBody));
}, error =>
{
// XXX
// uh oh, what happens?
}));
}
else if (request.Method.ToUpperInvariant() == "POST" && request.Uri.StartsWith("/echo"))
{
var headers = new HttpResponseHead()
{
Status = "200 OK",
Headers = new Dictionary<string, string>()
{
{ "Content-Type", "text/plain" },
{ "Connection", "close" }
}
};
if (request.Headers.ContainsKey("Content-Length"))
headers.Headers["Content-Length"] = request.Headers["Content-Length"];
// if you call OnResponse before subscribing to the request body,
// 100-continue will not be sent before the response is sent.
// per rfc2616 this response must have a 'final' status code,
// but the server does not enforce it.
response.OnResponse(headers, requestBody);
}*/
string body = Manager.webFront.processRequest(request);
var headers = new HttpResponseHead()
{
Status = "200 OK",
Headers = new Dictionary<string, string>()
{
{ "Content-Type", "text/html" },
{ "Content-Length", body.Length.ToString() },
}
};
response.OnResponse(headers, new BufferedProducer(body));
/*
if (request.Uri.StartsWith("/"))
{
var body = string.Format(
"Hello world.\r\nHello.\r\n\r\nUri: {0}\r\nPath: {1}\r\nQuery:{2}\r\nFragment: {3}\r\n",
request.Uri,
request.Path,
request.QueryString,
request.Fragment);
var headers = new HttpResponseHead()
{
Status = "200 OK",
Headers = new Dictionary<string, string>()
{
{ "Content-Type", "text/plain" },
{ "Content-Length", body.Length.ToString() },
}
};
response.OnResponse(headers, new BufferedProducer(body));
}
else
{
var responseBody = "The resource you requested ('" + request.Uri + "') could not be found.";
var headers = new HttpResponseHead()
{
Status = "404 Not Found",
Headers = new Dictionary<string, string>()
{
{ "Content-Type", "text/plain" },
{ "Content-Length", responseBody.Length.ToString() }
}
};
var body = new BufferedProducer(responseBody);
response.OnResponse(headers, body);
}*/
}
}
class BufferedProducer : IDataProducer
{
ArraySegment<byte> data;
public BufferedProducer(string data) : this(data, Encoding.UTF8) { }
public BufferedProducer(string data, Encoding encoding) : this(encoding.GetBytes(data)) { }
public BufferedProducer(byte[] data) : this(new ArraySegment<byte>(data)) { }
public BufferedProducer(ArraySegment<byte> data)
{
this.data = data;
}
public IDisposable Connect(IDataConsumer channel)
{
channel.OnData(data, null);
channel.OnEnd();
return null;
}
}
class BufferedConsumer : IDataConsumer
{
List<ArraySegment<byte>> buffer = new List<ArraySegment<byte>>();
Action<string> resultCallback;
Action<Exception> errorCallback;
public BufferedConsumer(Action<string> resultCallback, Action<Exception> errorCallback)
{
this.resultCallback = resultCallback;
this.errorCallback = errorCallback;
}
public bool OnData(ArraySegment<byte> data, Action continuation)
{
buffer.Add(data);
return false;
}
public void OnError(Exception error)
{
errorCallback(error);
}
public void OnEnd()
{
var str = buffer
.Select(b => Encoding.UTF8.GetString(b.Array, b.Offset, b.Count))
.Aggregate((result, next) => result + next);
resultCallback(str);
}
}
}

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{99E36EBD-1FA1-494C-8A66-BECE64EFF378}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Webfront_Plugin</RootNamespace>
<AssemblyName>Webfront Plugin</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Kayak">
<HintPath>..\Admin\bin\Release\lib\Kayak.dll</HintPath>
</Reference>
<Reference Include="SharedLibary">
<HintPath>..\SharedLibary\bin\Release\SharedLibary.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Framework.cs" />
<Compile Include="Main.cs" />
<Compile Include="Manager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)Admin\plugins\$(TargetName).dll"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>