Overwrite

Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
This commit is contained in:
Unstable Kitsune
2023-11-28 23:20:26 -05:00
parent 3cd54811de
commit b918192e4e
11608 changed files with 2644205 additions and 47 deletions

View File

@@ -0,0 +1,27 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System.Net;
#endregion
namespace VitaNex.Web
{
public delegate void WebAPIContextHandler(WebAPIContext context);
public delegate void WebAPIClientConnected(WebAPIClient client);
public delegate void WebAPIClientDisconnected(WebAPIClient client);
public delegate void WebAPIRequestSend<in T>(HttpWebRequest req, T state);
public delegate void WebAPIRequestReceive<in T>(HttpWebRequest req, T state, HttpWebResponse res);
}

View File

@@ -0,0 +1,411 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Sockets;
using System.Text;
#endregion
namespace VitaNex.Web
{
public sealed class WebAPIClient : IDisposable
{
private static readonly char[] _Separators = { '\r', '\n' };
private static readonly byte[] _EmptyBuffer = new byte[0];
private static readonly KeyValueString[] _EmptyHeaders = new KeyValueString[0];
private static IEnumerable<KeyValueString> ParseHeaders(IEnumerable<string> headers)
{
int i;
foreach (var h in headers)
{
i = h.IndexOf(' ');
if (i > 0)
{
var k = h.Substring(0, i).Trim().TrimEnd(':');
var v = h.Substring(i + 1);
yield return new KeyValueString(k, v);
}
}
}
public TcpClient Client { get; private set; }
public NetworkStream Stream { get; private set; }
public bool Connected => Client != null && Client.Connected;
public bool IsDisposed { get; private set; }
public WebAPIClient(TcpClient client)
{
Client = client;
Stream = Client.GetStream();
}
public void Encode(Encoding enc, string data, out byte[] buffer, out int length)
{
buffer = enc.GetBytes(data ?? String.Empty);
length = buffer.Length;
}
public void Decode(Encoding enc, byte[] buffer, int length, out string data)
{
if (buffer == null || buffer.Length == 0 || length <= 0)
{
data = String.Empty;
return;
}
data = enc.GetString(buffer, 0, length);
}
public void Compress(ref byte[] buffer, ref int length)
{
using (var outS = new MemoryStream())
{
using (var ds = new DeflateStream(outS, CompressionMode.Compress, true))
{
ds.Write(buffer, 0, length);
}
outS.Position = 0;
// Recycle the buffer?
if (outS.Length <= buffer.Length)
{
length = outS.Read(buffer, 0, buffer.Length);
// Heartbleed: Nope; zero-fill the remaining buffer!
for (var i = length; i < buffer.Length; i++)
{
buffer[i] = 0;
}
}
else
{
buffer = outS.ToArray();
length = buffer.Length;
}
}
}
public void Decompress(ref byte[] buffer, ref int length)
{
using (MemoryStream inS = new MemoryStream(buffer, 0, length), outS = new MemoryStream())
{
using (var ds = new DeflateStream(inS, CompressionMode.Decompress, true))
{
ds.CopyTo(outS);
}
outS.Position = 0;
// Recycle the buffer?
if (outS.Length <= buffer.Length)
{
length = outS.Read(buffer, 0, buffer.Length);
// Heartbleed: Nope; zero-fill the remaining buffer!
for (var i = length; i < buffer.Length; i++)
{
buffer[i] = 0;
}
}
else
{
buffer = outS.ToArray();
length = buffer.Length;
}
}
}
public int Send(bool compress, string data, Encoding enc)
{
Send(compress, data, enc, out var buffer, out var length);
return length;
}
public void Send(bool compress, string data, Encoding enc, out byte[] buffer, out int length)
{
Encode(enc, data, out buffer, out length);
Send(compress, ref buffer, ref length);
}
public void Send(bool compress, ref byte[] buffer, ref int length)
{
if (compress)
{
Compress(ref buffer, ref length);
}
if (buffer.Length > Client.SendBufferSize)
{
Client.SendBufferSize = Math.Min(1048576, buffer.Length);
}
Stream.Write(buffer, 0, length);
WebAPI.CSOptions.ToConsole(
"Sent {0:#,0} bytes ({1:#,0} bytes/write)",
length,
Math.Min(length, Client.SendBufferSize));
}
private static bool Sequence(byte b, ref int seq)
{
switch (b)
{
case 13:
{
if (seq % 2 == 0)
{
++seq;
}
else
{
seq = 0;
}
}
break;
case 10:
{
if (seq % 2 == 1)
{
++seq;
}
else
{
seq = 0;
}
}
break;
default:
seq = 0;
break;
}
return seq > 0;
}
public bool ReceiveHeaders(out KeyValueString[] headers)
{
headers = _EmptyHeaders;
var buffer = _EmptyBuffer;
var length = 0;
if (Stream.CanRead)
{
VitaNexCore.WaitWhile(() => !Stream.DataAvailable, TimeSpan.FromMilliseconds(1000));
if (Stream.DataAvailable)
{
buffer = new byte[Client.ReceiveBufferSize];
using (var ms = new MemoryStream())
{
int idx = 0, seq = 0;
while (Stream.DataAvailable)
{
var r = Stream.ReadByte();
if (r > -1)
{
if (++length > WebAPI.CSOptions.MaxReceiveBufferSizeBytes)
{
throw new InternalBufferOverflowException(
String.Format("Received data exceeded {0:#,0} bytes", WebAPI.CSOptions.MaxReceiveBufferSizeBytes));
}
var b = (byte)r;
buffer[idx++] = b;
if (Sequence(b, ref seq) && seq >= 4)
{
break;
}
if (idx >= buffer.Length)
{
ms.Write(buffer, 0, idx);
idx = 0;
}
}
}
if (idx > 0)
{
ms.Write(buffer, 0, idx);
}
buffer = ms.ToArray();
length = buffer.Length;
}
}
}
WebAPI.CSOptions.ToConsole(
"Received {0:#,0} bytes ({1:#,0} bytes/read)",
length,
Math.Min(length, Client.ReceiveBufferSize));
if (length <= 0)
{
return false;
}
var raw = Encoding.ASCII.GetString(buffer, 0, length);
if (String.IsNullOrWhiteSpace(raw))
{
return false;
}
var h = raw.Split(_Separators, StringSplitOptions.RemoveEmptyEntries);
if (h.Length == 0)
{
return false;
}
headers = ParseHeaders(h).ToArray();
return headers.Length > 0;
}
public void Receive(bool decompress, Encoding enc, out string content, out byte[] buffer, out int length)
{
content = String.Empty;
buffer = _EmptyBuffer;
length = 0;
if (Stream.CanRead)
{
VitaNexCore.WaitWhile(() => !Stream.DataAvailable, TimeSpan.FromMilliseconds(1000));
if (Stream.DataAvailable)
{
buffer = new byte[Client.ReceiveBufferSize];
using (var ms = new MemoryStream())
{
while (Stream.DataAvailable)
{
length = Stream.Read(buffer, 0, buffer.Length);
if (length > 0)
{
if (ms.Length + length > WebAPI.CSOptions.MaxReceiveBufferSizeBytes)
{
throw new InternalBufferOverflowException(
String.Format("Received data exceeded {0:#,0} bytes", WebAPI.CSOptions.MaxReceiveBufferSizeBytes));
}
ms.SetLength(ms.Length + length);
ms.Write(buffer, 0, length);
}
}
buffer = ms.ToArray();
length = buffer.Length;
}
}
}
WebAPI.CSOptions.ToConsole(
"Received {0:#,0} bytes ({1:#,0} bytes/read)",
length,
Math.Min(length, Client.ReceiveBufferSize));
if (length > 0)
{
if (decompress)
{
Decompress(ref buffer, ref length);
}
Decode(enc, buffer, length, out content);
}
}
public void Close()
{
Close(false);
}
public void Close(bool disconnecting)
{
if (IsDisposed || !Client.Connected)
{
return;
}
VitaNexCore.TryCatch(
() =>
{
if (!disconnecting)
{
WebAPI.Disconnect(this);
}
if (Stream != null)
{
using (Stream)
{
Stream.Close();
}
}
if (Client != null)
{
Client.Close();
}
},
WebAPI.CSOptions.ToConsole);
}
public void Dispose()
{
if (IsDisposed)
{
return;
}
Close(false);
IsDisposed = true;
Client = null;
}
public override string ToString()
{
if (Client == null || Client.Client == null || Client.Client.RemoteEndPoint == null)
{
return "?.?.?.?:?";
}
return Client.Client.RemoteEndPoint.ToString();
}
}
}

View File

@@ -0,0 +1,55 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
#endregion
namespace VitaNex.Web
{
public class WebAPIContext : IDisposable
{
public WebAPIClient Client { get; private set; }
public WebAPIMethod Method { get; private set; }
public string Uri { get; private set; }
public WebAPIRequest Request { get; private set; }
public WebAPIResponse Response { get; private set; }
public bool Authorized { get; set; }
public WebAPIContext(WebAPIClient client, WebAPIMethod method, string uri)
{
Client = client;
Method = method;
Uri = uri;
Request = new WebAPIRequest(Client);
Response = new WebAPIResponse(Client);
}
public void Dispose()
{
Client = null;
Method = WebAPIMethod.UNKNOWN;
Uri = null;
Request.Dispose();
Request = null;
Response.Dispose();
Response = null;
}
}
}

View File

@@ -0,0 +1,35 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
#endregion
namespace VitaNex.Web
{
public class WebAPIHandler
{
public string Uri { get; private set; }
public Action<WebAPIContext> Handler { get; set; }
public WebAPIHandler(string uri, Action<WebAPIContext> handler)
{
Uri = uri;
Handler = handler;
}
public override int GetHashCode()
{
return Uri.GetHashCode();
}
}
}

View File

@@ -0,0 +1,82 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
#endregion
namespace VitaNex.Web
{
public class WebAPIHeaders : IDisposable, IEnumerable<KeyValuePair<string, string>>
{
private Dictionary<string, string> _Headers;
public string this[string header]
{
get => _Headers.GetValue(header);
set
{
if (value == null)
{
_Headers.Remove(header);
}
else
{
_Headers[header] = value;
}
}
}
public int Count => _Headers.Count;
public WebAPIHeaders()
{
_Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
public WebAPIHeaders(IEnumerable<KeyValuePair<string, string>> headers)
: this()
{
foreach (var kv in headers.Ensure())
{
this[kv.Key] = kv.Value;
}
}
public void Clear()
{
_Headers.Clear();
}
public void Dispose()
{
_Headers = null;
}
public override string ToString()
{
return String.Join("\r\n", _Headers.Select(kv => String.Format("{0}: {1}", kv.Key, kv.Value))) + "\r\n\r\n";
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return _Headers.GetEnumerator();
}
}
}

View File

@@ -0,0 +1,26 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
namespace VitaNex.Web
{
public enum WebAPIMethod
{
UNKNOWN = 0,
OPTIONS,
GET,
HEAD,
POST,
PUT,
DELETE,
TRACE,
CONNECT
}
}

View File

@@ -0,0 +1,85 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections;
using System.Collections.Generic;
#endregion
namespace VitaNex.Web
{
public class WebAPIQueries : IDisposable, IEnumerable<KeyValuePair<string, string>>
{
private Dictionary<string, string> _Queries;
public string this[string query]
{
get => _Queries.GetValue(query);
set
{
if (value == null)
{
_Queries.Remove(query);
}
else
{
_Queries[query] = value;
}
}
}
public int Count => _Queries.Count;
public WebAPIQueries()
{
_Queries = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
public WebAPIQueries(IEnumerable<KeyValuePair<string, string>> queries)
: this()
{
foreach (var kv in queries.Ensure())
{
this[kv.Key] = kv.Value;
}
}
public WebAPIQueries(string query)
: this(WebAPI.DecodeQuery(query))
{ }
public void Clear()
{
_Queries.Clear();
}
public void Dispose()
{
_Queries = null;
}
public override string ToString()
{
return WebAPI.EncodeQuery(_Queries);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return _Queries.GetEnumerator();
}
}
}

View File

@@ -0,0 +1,63 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Text;
using VitaNex.IO;
#endregion
namespace VitaNex.Web
{
public class WebAPIRequest : IDisposable
{
public WebAPIClient Client { get; private set; }
public WebAPIHeaders Headers { get; private set; }
public WebAPIQueries Queries { get; private set; }
public FileMime ContentType { get; set; }
public Encoding Encoding { get; set; }
public string Data { get; set; }
public int Length { get; set; }
public WebAPIRequest(WebAPIClient client)
{
Client = client;
Headers = new WebAPIHeaders();
Queries = new WebAPIQueries();
ContentType = FileMime.Default;
Encoding = Encoding.UTF8;
Data = String.Empty;
Length = 0;
}
public void Dispose()
{
Headers.Dispose();
Headers = null;
Queries.Dispose();
Queries = null;
Encoding = null;
Data = null;
Length = 0;
Client = null;
}
}
}

View File

@@ -0,0 +1,74 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Net;
using System.Text;
using VitaNex.IO;
#endregion
namespace VitaNex.Web
{
public class WebAPIResponse : IDisposable
{
public WebAPIClient Client { get; private set; }
public WebAPIHeaders Headers { get; private set; }
public HttpStatusCode Status { get; set; }
public FileMime ContentType { get; set; }
public Encoding Encoding { get; set; }
public object Data { get; set; }
public bool Compress { get; set; }
public bool FreeData { get; set; }
public string FileName { get; set; }
public int Cache { get; set; }
public WebAPIResponse(WebAPIClient client)
{
Client = client;
Headers = new WebAPIHeaders();
Compress = false;
FreeData = true;
Cache = -1;
FileName = String.Empty;
Encoding = Encoding.UTF8;
Status = HttpStatusCode.OK;
ContentType = FileMime.Default;
}
public void Dispose()
{
if (FreeData && Data is IDisposable)
{
((IDisposable)Data).Dispose();
}
Data = null;
Headers.Dispose();
Headers = null;
Cache = -1;
FileName = "public.html";
Encoding = null;
Client = null;
}
}
}

View File

@@ -0,0 +1,194 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System.Collections.Generic;
using Server;
#endregion
namespace VitaNex.Web
{
public class WebAPIOptions : CoreServiceOptions
{
public List<string> Whitelist { get; private set; }
public List<string> Blacklist { get; private set; }
[CommandProperty(WebAPI.Access)]
public bool UseWhitelist { get; set; }
[CommandProperty(WebAPI.Access)]
public short Port { get; set; }
[CommandProperty(WebAPI.Access)]
public int MaxConnections { get; set; }
[CommandProperty(WebAPI.Access)]
public int MaxSendBufferSize { get; set; }
[CommandProperty(WebAPI.Access)]
public int MaxReceiveBufferSize { get; set; }
[CommandProperty(WebAPI.Access)]
public int MaxSendBufferSizeBytes => MaxSendBufferSize * 1024 * 1024;
[CommandProperty(WebAPI.Access)]
public int MaxReceiveBufferSizeBytes => MaxReceiveBufferSize * 1024 * 1024;
[CommandProperty(WebAPI.Access)]
public bool DirectoryIndex { get; set; }
[CommandProperty(WebAPI.Access)]
public bool WebServer { get; set; }
public WebAPIOptions()
: base(typeof(WebAPI))
{
Whitelist = new List<string>();
Blacklist = new List<string>();
Port = 2595;
MaxConnections = 500;
MaxSendBufferSize = 32;
MaxReceiveBufferSize = 32;
UseWhitelist = false;
WebServer = true;
DirectoryIndex = true;
}
public WebAPIOptions(GenericReader reader)
: base(reader)
{ }
public override void Clear()
{
base.Clear();
Whitelist.Clear();
Blacklist.Clear();
Port = 80;
MaxConnections = 500;
MaxSendBufferSize = 32;
MaxReceiveBufferSize = 32;
UseWhitelist = false;
WebServer = false;
DirectoryIndex = false;
}
public override void Reset()
{
base.Reset();
Whitelist.Clear();
Blacklist.Clear();
Port = 80;
MaxConnections = 500;
MaxSendBufferSize = 32;
MaxReceiveBufferSize = 32;
UseWhitelist = false;
WebServer = false;
DirectoryIndex = false;
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
var version = writer.SetVersion(4);
switch (version)
{
case 4:
{
writer.Write(WebServer);
writer.Write(DirectoryIndex);
}
goto case 3;
case 3:
case 2:
{
writer.Write(MaxSendBufferSize);
writer.Write(MaxReceiveBufferSize);
}
goto case 1;
case 1:
{
writer.WriteList(Whitelist, (w, m) => w.Write(m));
writer.WriteList(Blacklist, (w, m) => w.Write(m));
writer.Write(UseWhitelist);
}
goto case 0;
case 0:
{
writer.Write(Port);
writer.Write(MaxConnections);
}
break;
}
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
var version = reader.GetVersion();
if (version < 3)
{
MaxSendBufferSize = 32;
MaxReceiveBufferSize = 32;
}
if (version < 2)
{
Whitelist = new List<string>();
Blacklist = new List<string>();
}
switch (version)
{
case 4:
{
WebServer = reader.ReadBool();
DirectoryIndex = reader.ReadBool();
}
goto case 3;
case 3:
case 2:
{
MaxSendBufferSize = reader.ReadInt();
MaxReceiveBufferSize = reader.ReadInt();
}
goto case 1;
case 1:
{
Whitelist = reader.ReadList(r => r.ReadString(), Whitelist);
Blacklist = reader.ReadList(r => r.ReadString(), Blacklist);
UseWhitelist = reader.ReadBool();
}
goto case 0;
case 0:
{
Port = reader.ReadShort();
MaxConnections = reader.ReadInt();
}
break;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,100 @@
#region Header
// _,-'/-'/
// . __,-; ,'( '/
// \. `-.__`-._`:_,-._ _ , . ``
// `:-._,------' ` _,`--` -: `_ , ` ,' :
// `---..__,,--' (C) 2023 ` -'. -'
// # Vita-Nex [http://core.vita-nex.com] #
// {o)xxx|===============- # -===============|xxx(o}
// # #
#endregion
#region References
using System;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using Server;
using Server.Misc;
#endregion
namespace VitaNex.Web
{
[CoreService("Web API", "3.0.0.1")]
public static partial class WebAPI
{
static WebAPI()
{
CSOptions = new WebAPIOptions();
Clients = new List<WebAPIClient>();
Handlers = new Dictionary<string, WebAPIHandler>(StringComparer.OrdinalIgnoreCase);
_ActivityTimer = PollTimer.FromSeconds(
60.0,
() =>
{
if (!_Listening || Listener == null || Listener.Server == null || !Listener.Server.IsBound)
{
_Listening = false;
ListenerUtility.ListenAsync();
}
Clients.RemoveAll(c => !c.Connected);
},
() => Clients.Count > 0,
false);
ServicePointManager.SecurityProtocol = (SecurityProtocolType)0x3FC0; // Tls, Tls11, Tls12, Tls13
}
private static void CSConfig()
{
if (_ServerStarted)
{
ListenerUtility.ListenAsync();
return;
}
EventSink.ServerStarted += () =>
{
_ServerStarted = true;
ListenerUtility.ListenAsync();
};
var t = typeof(StatusPage);
var f = t.GetField("Enabled", BindingFlags.Public | BindingFlags.Static);
if (f != null)
{
f.SetValue(null, false);
}
}
private static void CSInvoke()
{
_ActivityTimer.Start();
Register("/", HandleRoot);
}
private static void CSDisposed()
{
Clients.ForEachReverse(c => c.Close());
_ActivityTimer.Stop();
_ActivityTimer = null;
if (Listener == null)
{
return;
}
Listener.Stop();
Listener = null;
}
}
}