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,288 @@
using System;
using System.Collections;
using System.Text;
using Server.Accounting;
using Server.Network;
namespace Server.RemoteAdmin
{
public class AdminNetwork
{
private const string ProtocolVersion = "2";
private static ArrayList m_Auth = new ArrayList();
private static bool m_NewLine = true;
private static readonly StringBuilder m_ConsoleData = new StringBuilder();
private const string DateFormat = "MMMM dd hh:mm:ss.f tt";
public static void Configure()
{
PacketHandlers.Register(0xF1, 0, false, new OnPacketReceive(OnReceive));
Core.MultiConsoleOut.Add(new EventTextWriter(new EventTextWriter.OnConsoleChar(OnConsoleChar), new EventTextWriter.OnConsoleLine(OnConsoleLine), new EventTextWriter.OnConsoleStr(OnConsoleString)));
Timer.DelayCall(TimeSpan.FromMinutes(2.5), TimeSpan.FromMinutes(2.5), new TimerCallback(CleanUp));
}
public static void OnConsoleString(string str)
{
string outStr;
if (m_NewLine)
{
outStr = String.Format("[{0}]: {1}", DateTime.UtcNow.ToString(DateFormat), str);
m_NewLine = false;
}
else
{
outStr = str;
}
m_ConsoleData.Append(outStr);
RoughTrimConsoleData();
SendToAll(outStr);
}
public static void OnConsoleChar(char ch)
{
if (m_NewLine)
{
string outStr;
outStr = String.Format("[{0}]: {1}", DateTime.UtcNow.ToString(DateFormat), ch);
m_ConsoleData.Append(outStr);
SendToAll(outStr);
m_NewLine = false;
}
else
{
m_ConsoleData.Append(ch);
SendToAll(ch);
}
RoughTrimConsoleData();
}
public static void OnConsoleLine(string line)
{
string outStr;
if (m_NewLine)
outStr = String.Format("[{0}]: {1}{2}", DateTime.UtcNow.ToString(DateFormat), line, Console.Out.NewLine);
else
outStr = String.Format("{0}{1}", line, Console.Out.NewLine);
m_ConsoleData.Append(outStr);
RoughTrimConsoleData();
SendToAll(outStr);
m_NewLine = true;
}
static void SendToAll(string outStr)
{
SendToAll(new ConsoleData(outStr));
}
static void SendToAll(char ch)
{
SendToAll(new ConsoleData(ch));
}
static void SendToAll(ConsoleData packet)
{
packet.Acquire();
for (int i = 0; i < m_Auth.Count; i++)
((NetState)m_Auth[i]).Send(packet);
packet.Release();
}
static void RoughTrimConsoleData()
{
if (m_ConsoleData.Length >= 4096)
m_ConsoleData.Remove(0, 2048);
}
static void TightTrimConsoleData()
{
if (m_ConsoleData.Length > 1024)
m_ConsoleData.Remove(0, m_ConsoleData.Length - 1024);
}
public static void OnReceive(NetState state, PacketReader pvSrc)
{
byte cmd = pvSrc.ReadByte();
if (cmd == 0x02)
{
Authenticate(state, pvSrc);
}
else if (cmd == 0xFE)
{
state.Send(new CompactServerInfo());
state.Dispose();
}
else if (cmd == 0xFF)
{
string statStr = String.Format(", Name={0}, Age={1}, Clients={2}, Items={3}, Chars={4}, Mem={5}K, Ver={6}", Server.Misc.ServerList.ServerName, (int)(DateTime.UtcNow - Server.Items.Clock.ServerStart).TotalHours, NetState.Instances.Count, World.Items.Count, World.Mobiles.Count, (int)(System.GC.GetTotalMemory(false) / 1024), ProtocolVersion);
state.Send(new UOGInfo(statStr));
state.Dispose();
}
else if (!IsAuth(state))
{
Console.WriteLine("ADMIN: Unauthorized packet from {0}, disconnecting", state);
Disconnect(state);
}
else
{
if (!RemoteAdminHandlers.Handle(cmd, state, pvSrc))
Disconnect(state);
}
}
private static void DelayedDisconnect(NetState state)
{
Timer.DelayCall(TimeSpan.FromSeconds(15.0), new TimerStateCallback(Disconnect), state);
}
private static void Disconnect(object state)
{
m_Auth.Remove(state);
((NetState)state).Dispose();
}
public static void Authenticate(NetState state, PacketReader pvSrc)
{
string user = pvSrc.ReadString(30);
string pw = pvSrc.ReadString(30);
Account a = Accounts.GetAccount(user) as Account;
if (a == null)
{
state.Send(new Login(LoginResponse.NoUser));
Console.WriteLine("ADMIN: Invalid username '{0}' from {1}", user, state);
DelayedDisconnect(state);
}
else if (!a.HasAccess(state))
{
state.Send(new Login(LoginResponse.BadIP));
Console.WriteLine("ADMIN: Access to '{0}' from {1} denied.", user, state);
DelayedDisconnect(state);
}
else if (!a.CheckPassword(pw))
{
state.Send(new Login(LoginResponse.BadPass));
Console.WriteLine("ADMIN: Invalid password for user '{0}' from {1}", user, state);
DelayedDisconnect(state);
}
else if (a.AccessLevel < AccessLevel.Administrator || a.Banned)
{
Console.WriteLine("ADMIN: Account '{0}' does not have admin access. Connection Denied.", user);
state.Send(new Login(LoginResponse.NoAccess));
DelayedDisconnect(state);
}
else
{
Console.WriteLine("ADMIN: Access granted to '{0}' from {1}", user, state);
state.Account = a;
a.LogAccess(state);
a.LastLogin = DateTime.UtcNow;
state.Send(new Login(LoginResponse.OK));
TightTrimConsoleData();
state.Send(Compress(new ConsoleData(m_ConsoleData.ToString())));
m_Auth.Add(state);
}
}
public static bool IsAuth(NetState state)
{
return m_Auth.Contains(state);
}
private static void CleanUp()
{ //remove dead instances from m_Auth
ArrayList list = new ArrayList();
for (int i = 0; i < m_Auth.Count; i++)
{
NetState ns = (NetState)m_Auth[i];
if (ns.Running)
list.Add(ns);
}
m_Auth = list;
}
public static Packet Compress(Packet p)
{
int length;
byte[] source = p.Compile(false, out length);
if (length > 100 && length < 60000)
{
byte[] dest = new byte[(int)(length * 1.001) + 10];
int destSize = dest.Length;
ZLibError error = Compression.Pack(dest, ref destSize, source, length, ZLibQuality.Default);
if (error != ZLibError.Okay)
{
Console.WriteLine("WARNING: Unable to compress admin packet, zlib error: {0}", error);
return p;
}
else
{
return new AdminCompressedPacket(dest, destSize, length);
}
}
else
{
return p;
}
}
}
public class EventTextWriter : System.IO.TextWriter
{
public delegate void OnConsoleChar(char ch);
public delegate void OnConsoleLine(string line);
public delegate void OnConsoleStr(string str);
private readonly OnConsoleChar m_OnChar;
private readonly OnConsoleLine m_OnLine;
private readonly OnConsoleStr m_OnStr;
public EventTextWriter(OnConsoleChar onChar, OnConsoleLine onLine, OnConsoleStr onStr)
{
this.m_OnChar = onChar;
this.m_OnLine = onLine;
this.m_OnStr = onStr;
}
public override void Write(char ch)
{
if (this.m_OnChar != null)
this.m_OnChar(ch);
}
public override void Write(string str)
{
if (this.m_OnStr != null)
this.m_OnStr(str);
}
public override void WriteLine(string line)
{
if (this.m_OnLine != null)
this.m_OnLine(line);
}
public override System.Text.Encoding Encoding
{
get
{
return System.Text.Encoding.ASCII;
}
}
}
}

View File

@@ -0,0 +1,239 @@
using System;
using System.Collections;
using Server.Accounting;
using Server.Network;
namespace Server.RemoteAdmin
{
public class RemoteAdminHandlers
{
private static readonly OnPacketReceive[] m_Handlers = new OnPacketReceive[256];
static RemoteAdminHandlers()
{
//0x02 = login request, handled by AdminNetwork
Register(0x04, new OnPacketReceive(ServerInfoRequest));
Register(0x05, new OnPacketReceive(AccountSearch));
Register(0x06, new OnPacketReceive(RemoveAccount));
Register(0x07, new OnPacketReceive(UpdateAccount));
}
public enum AcctSearchType : byte
{
Username = 0,
IP = 1,
}
public static void Register(byte command, OnPacketReceive handler)
{
m_Handlers[command] = handler;
}
public static bool Handle(byte command, NetState state, PacketReader pvSrc)
{
if (m_Handlers[command] == null)
{
Console.WriteLine("ADMIN: Invalid packet 0x{0:X2} from {1}, disconnecting", command, state);
return false;
}
else
{
m_Handlers[command](state, pvSrc);
return true;
}
}
private static void ServerInfoRequest(NetState state, PacketReader pvSrc)
{
state.Send(AdminNetwork.Compress(new ServerInfo()));
}
private static void AccountSearch(NetState state, PacketReader pvSrc)
{
AcctSearchType type = (AcctSearchType)pvSrc.ReadByte();
string term = pvSrc.ReadString();
if (type == AcctSearchType.IP && !Utility.IsValidIP(term))
{
state.Send(new MessageBoxMessage("Invalid search term.\nThe IP sent was not valid.", "Invalid IP"));
return;
}
else
{
term = term.ToUpper();
}
ArrayList list = new ArrayList();
foreach (Account a in Accounts.GetAccounts())
{
if (!CanAccessAccount(state.Account, a))
continue;
switch ( type )
{
case AcctSearchType.Username:
{
if (a.Username.ToUpper().IndexOf(term) != -1)
list.Add(a);
break;
}
case AcctSearchType.IP:
{
for (int i = 0; i < a.LoginIPs.Length; i++)
{
if (Utility.IPMatch(term, a.LoginIPs[i]))
{
list.Add(a);
break;
}
}
break;
}
}
}
if (list.Count > 0)
{
if (list.Count <= 25)
state.Send(AdminNetwork.Compress(new AccountSearchResults(list)));
else
state.Send(new MessageBoxMessage("There were more than 25 matches to your search.\nNarrow the search parameters and try again.", "Too Many Results"));
}
else
{
state.Send(new MessageBoxMessage("There were no results to your search.\nPlease try again.", "No Matches"));
}
}
static bool CanAccessAccount(IAccount beholder, IAccount beheld)
{
return beholder.AccessLevel == AccessLevel.Owner || beheld.AccessLevel < beholder.AccessLevel; // Cannot see accounts of equal or greater access level unless Owner
}
private static void RemoveAccount(NetState state, PacketReader pvSrc)
{
if (state.Account.AccessLevel < AccessLevel.Administrator)
{
state.Send(new MessageBoxMessage("You do not have permission to delete accounts.", "Account Access Exception"));
return;
}
IAccount a = Accounts.GetAccount(pvSrc.ReadString());
if (a == null)
{
state.Send(new MessageBoxMessage("The account could not be found (and thus was not deleted).", "Account Not Found"));
}
else if (!CanAccessAccount(state.Account, a))
{
state.Send(new MessageBoxMessage("You cannot delete an account with an access level greater than or equal to your own.", "Account Access Exception"));
}
else if (a == state.Account)
{
state.Send(new MessageBoxMessage("You may not delete your own account.", "Not Allowed"));
}
else
{
RemoteAdminLogging.WriteLine(state, "Deleted Account {0}", a);
a.Delete();
state.Send(new MessageBoxMessage("The requested account (and all it's characters) has been deleted.", "Account Deleted"));
}
}
private static void UpdateAccount(NetState state, PacketReader pvSrc)
{
if (state.Account.AccessLevel < AccessLevel.Administrator)
{
state.Send(new MessageBoxMessage("You do not have permission to edit accounts.", "Account Access Exception"));
return;
}
string username = pvSrc.ReadString();
string pass = pvSrc.ReadString();
Account a = Accounts.GetAccount(username) as Account;
if (a != null && !CanAccessAccount(state.Account, a))
{
state.Send(new MessageBoxMessage("You cannot edit an account with an access level greater than or equal to your own.", "Account Access Exception"));
}
else
{
bool CreatedAccount = false;
bool UpdatedPass = false;
bool oldbanned = a == null ? false : a.Banned;
AccessLevel oldAcessLevel = a == null ? 0 : a.AccessLevel;
if (a == null)
{
a = new Account(username, pass);
CreatedAccount = true;
}
else if (pass != "(hidden)")
{
a.SetPassword(pass);
UpdatedPass = true;
}
if (a != state.Account)
{
AccessLevel newAccessLevel = (AccessLevel)pvSrc.ReadByte();
if (a.AccessLevel != newAccessLevel)
{
if (newAccessLevel >= state.Account.AccessLevel)
state.Send(new MessageBoxMessage("Warning: You may not set an access level greater than or equal to your own.", "Account Access Level update denied."));
else
a.AccessLevel = newAccessLevel;
}
bool newBanned = pvSrc.ReadBoolean();
if (newBanned != a.Banned)
{
oldbanned = a.Banned;
a.Banned = newBanned;
a.Comments.Add(new AccountComment(state.Account.Username, newBanned ? "Banned via Remote Admin" : "Unbanned via Remote Admin"));
}
}
else
{
pvSrc.ReadInt16();//skip both
state.Send(new MessageBoxMessage("Warning: When editing your own account, Account Status and Access Level cannot be changed.", "Editing Own Account"));
}
ArrayList list = new ArrayList();
ushort length = pvSrc.ReadUInt16();
bool invalid = false;
for (int i = 0; i < length; i++)
{
string add = pvSrc.ReadString();
if (Utility.IsValidIP(add))
list.Add(add);
else
invalid = true;
}
if (list.Count > 0)
a.IPRestrictions = (string[])list.ToArray(typeof(string));
else
a.IPRestrictions = new string[0];
if (invalid)
state.Send(new MessageBoxMessage("Warning: one or more of the IP Restrictions you specified was not valid.", "Invalid IP Restriction"));
if (CreatedAccount)
RemoteAdminLogging.WriteLine(state, "Created account {0} with Access Level {1}", a.Username, a.AccessLevel);
else
{
string changes = string.Empty;
if (UpdatedPass)
changes += " Password Changed.";
if (oldAcessLevel != a.AccessLevel)
changes = string.Format("{0} Access level changed from {1} to {2}.", changes, oldAcessLevel, a.AccessLevel);
if (oldbanned != a.Banned)
changes += a.Banned ? " Banned." : " Unbanned.";
RemoteAdminLogging.WriteLine(state, "Updated account {0}:{1}", a.Username, changes);
}
state.Send(new MessageBoxMessage("Account updated successfully.", "Account Updated"));
}
}
}
}

View File

@@ -0,0 +1,172 @@
using System;
using System.Collections;
using Server.Accounting;
using Server.Items;
using Server.Network;
namespace Server.RemoteAdmin
{
public enum LoginResponse : byte
{
NoUser = 0,
BadIP,
BadPass,
NoAccess,
OK
}
public sealed class AdminCompressedPacket : Packet
{
public AdminCompressedPacket(byte[] CompData, int CDLen, int unCompSize)
: base(0x01)
{
this.EnsureCapacity(1 + 2 + 2 + CDLen);
this.m_Stream.Write((ushort)unCompSize);
this.m_Stream.Write(CompData, 0, CDLen);
}
}
public sealed class Login : Packet
{
public Login(LoginResponse resp)
: base(0x02, 2)
{
this.m_Stream.Write((byte)resp);
}
}
public sealed class ConsoleData : Packet
{
public ConsoleData(string str)
: base(0x03)
{
this.EnsureCapacity(1 + 2 + 1 + str.Length + 1);
this.m_Stream.Write((byte)2);
this.m_Stream.WriteAsciiNull(str);
}
public ConsoleData(char ch)
: base(0x03)
{
this.EnsureCapacity(1 + 2 + 1 + 1);
this.m_Stream.Write((byte)3);
this.m_Stream.Write((byte)ch);
}
}
public sealed class ServerInfo : Packet
{
public ServerInfo()
: base(0x04)
{
string netVer = Environment.Version.ToString();
string os = Environment.OSVersion.ToString();
this.EnsureCapacity(1 + 2 + (10 * 4) + netVer.Length + 1 + os.Length + 1);
int banned = 0;
int active = 0;
foreach (Account acct in Accounts.GetAccounts())
{
if (acct.Banned)
++banned;
else
++active;
}
this.m_Stream.Write((int)active);
this.m_Stream.Write((int)banned);
this.m_Stream.Write((int)Firewall.List.Count);
this.m_Stream.Write((int)NetState.Instances.Count);
this.m_Stream.Write((int)World.Mobiles.Count);
this.m_Stream.Write((int)Core.ScriptMobiles);
this.m_Stream.Write((int)World.Items.Count);
this.m_Stream.Write((int)Core.ScriptItems);
this.m_Stream.Write((uint)(DateTime.UtcNow - Clock.ServerStart).TotalSeconds);
this.m_Stream.Write((uint)GC.GetTotalMemory(false)); // TODO: uint not sufficient for TotalMemory (long). Fix protocol.
this.m_Stream.WriteAsciiNull(netVer);
this.m_Stream.WriteAsciiNull(os);
}
}
public sealed class AccountSearchResults : Packet
{
public AccountSearchResults(ArrayList results)
: base(0x05)
{
this.EnsureCapacity(1 + 2 + 2);
this.m_Stream.Write((byte)results.Count);
foreach (Account a in results)
{
this.m_Stream.WriteAsciiNull(a.Username);
string pwToSend = a.PlainPassword;
if (pwToSend == null)
pwToSend = "(hidden)";
this.m_Stream.WriteAsciiNull(pwToSend);
this.m_Stream.Write((byte)a.AccessLevel);
this.m_Stream.Write(a.Banned);
unchecked
{
this.m_Stream.Write((uint)a.LastLogin.Ticks);
}// TODO: This doesn't work, uint.MaxValue is only 7 minutes of ticks. Fix protocol.
this.m_Stream.Write((ushort)a.LoginIPs.Length);
for (int i = 0; i < a.LoginIPs.Length; i++)
this.m_Stream.WriteAsciiNull(a.LoginIPs[i].ToString());
this.m_Stream.Write((ushort)a.IPRestrictions.Length);
for (int i = 0; i < a.IPRestrictions.Length; i++)
this.m_Stream.WriteAsciiNull(a.IPRestrictions[i]);
}
}
}
public sealed class CompactServerInfo : Packet
{
public CompactServerInfo()
: base(0x51)
{
this.EnsureCapacity(1 + 2 + (4 * 4) + 8);
this.m_Stream.Write((int)NetState.Instances.Count - 1); // Clients
this.m_Stream.Write((int)World.Items.Count); // Items
this.m_Stream.Write((int)World.Mobiles.Count); // Mobiles
this.m_Stream.Write((uint)(DateTime.UtcNow - Clock.ServerStart).TotalSeconds); // Age (seconds)
long memory = GC.GetTotalMemory(false);
this.m_Stream.Write((uint)(memory >> 32)); // Memory high bytes
this.m_Stream.Write((uint)memory); // Memory low bytes
}
}
public sealed class UOGInfo : Packet
{
public UOGInfo(string str)
: base(0x53, str.Length + 7)// 'S'
{
this.m_Stream.WriteAsciiFixed("ervUO", 5);
this.m_Stream.WriteAsciiNull(str);
}
}
public sealed class MessageBoxMessage : Packet
{
public MessageBoxMessage(string msg, string caption)
: base(0x08)
{
this.EnsureCapacity(1 + 2 + msg.Length + 1 + caption.Length + 1);
this.m_Stream.WriteAsciiNull(msg);
this.m_Stream.WriteAsciiNull(caption);
}
}
}

View File

@@ -0,0 +1,114 @@
using System;
using System.IO;
using Server.Accounting;
using Server.Network;
namespace Server.RemoteAdmin
{
public class RemoteAdminLogging
{
private static StreamWriter m_Output;
private static bool m_Enabled = true;
private static bool Initialized = false;
const string LogBaseDirectory = "Logs";
const string LogSubDirectory = "RemoteAdmin";
public static bool Enabled
{
get
{
return m_Enabled;
}
set
{
m_Enabled = value;
}
}
public static StreamWriter Output
{
get
{
return m_Output;
}
}
public static void LazyInitialize()
{
if (Initialized || !m_Enabled)
return;
Initialized = true;
if (!Directory.Exists(LogBaseDirectory))
Directory.CreateDirectory(LogBaseDirectory);
string directory = Path.Combine(LogBaseDirectory, LogSubDirectory);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
try
{
m_Output = new StreamWriter(Path.Combine(directory, String.Format(LogSubDirectory + "{0}.log", DateTime.UtcNow.ToString("yyyyMMdd"))), true);
m_Output.AutoFlush = true;
m_Output.WriteLine("##############################");
m_Output.WriteLine("Log started on {0}", DateTime.UtcNow);
m_Output.WriteLine();
}
catch
{
Utility.PushColor(ConsoleColor.Red);
Console.WriteLine("RemoteAdminLogging: Failed to initialize LogWriter.");
Utility.PopColor();
m_Enabled = false;
}
}
public static object Format(object o)
{
o = Commands.CommandLogging.Format(o);
if (o == null)
return "(null)";
return o;
}
public static void WriteLine(NetState state, string format, params object[] args)
{
for (int i = 0; i < args.Length; i++)
args[i] = Commands.CommandLogging.Format(args[i]);
WriteLine(state, String.Format(format, args));
}
public static void WriteLine(NetState state, string text)
{
LazyInitialize();
if (!m_Enabled)
return;
try
{
Account acct = state.Account as Account;
string name = acct == null ? "(UNKNOWN)" : acct.Username;
string accesslevel = acct == null ? "NoAccount" : acct.AccessLevel.ToString();
string statestr = state == null ? "NULLSTATE" : state.ToString();
m_Output.WriteLine("{0}: {1}: {2}: {3}", DateTime.UtcNow, statestr, name, text);
string path = Core.BaseDirectory;
Commands.CommandLogging.AppendPath(ref path, LogBaseDirectory);
Commands.CommandLogging.AppendPath(ref path, LogSubDirectory);
Commands.CommandLogging.AppendPath(ref path, accesslevel);
path = Path.Combine(path, String.Format("{0}.log", name));
using (StreamWriter sw = new StreamWriter(path, true))
sw.WriteLine("{0}: {1}: {2}", DateTime.UtcNow, statestr, text);
}
catch
{
}
}
}
}