372 lines
No EOL
10 KiB
C#
372 lines
No EOL
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Net;
|
|
using Server;
|
|
using Server.Accounting;
|
|
using Server.Commands;
|
|
using Server.Engines.Help;
|
|
using Server.Network;
|
|
using Server.Regions;
|
|
|
|
namespace Server.Misc
|
|
{
|
|
public enum PasswordProtection
|
|
{
|
|
None,
|
|
Crypt,
|
|
NewCrypt
|
|
}
|
|
|
|
public class AccountHandler
|
|
{
|
|
private static int MaxAccountsPerIP = Server.Misc.Settings.MaxAccountsPerIP();
|
|
private static bool AutoAccountCreation = Server.Misc.Settings.AutoAccountCreation();
|
|
private static bool RestrictDeletion = Server.Misc.Settings.RestrictDeletion();
|
|
private static TimeSpan DeleteDelay = TimeSpan.FromDays( Server.Misc.Settings.DeleteDelay() );
|
|
|
|
public static PasswordProtection ProtectPasswords = PasswordProtection.NewCrypt;
|
|
|
|
private static AccessLevel m_LockdownLevel;
|
|
|
|
public static AccessLevel LockdownLevel
|
|
{
|
|
get{ return m_LockdownLevel; }
|
|
set{ m_LockdownLevel = value; }
|
|
}
|
|
|
|
private static CityInfo[] StartingCities = new CityInfo[]
|
|
{
|
|
new CityInfo( " ", " ", 1150168, 2081, 1585, 0 ),
|
|
};
|
|
|
|
private static bool PasswordCommandEnabled = true;
|
|
|
|
public static void Initialize()
|
|
{
|
|
EventSink.DeleteRequest += new DeleteRequestEventHandler( EventSink_DeleteRequest );
|
|
EventSink.AccountLogin += new AccountLoginEventHandler( EventSink_AccountLogin );
|
|
EventSink.GameLogin += new GameLoginEventHandler( EventSink_GameLogin );
|
|
|
|
if ( PasswordCommandEnabled )
|
|
CommandSystem.Register( "Password", AccessLevel.Player, new CommandEventHandler( Password_OnCommand ) );
|
|
}
|
|
|
|
[Usage( "Password <newPassword> <repeatPassword>" )]
|
|
[Description( "Changes the password of the commanding players account. Requires the same C-class IP address as the account's creator." )]
|
|
public static void Password_OnCommand( CommandEventArgs e )
|
|
{
|
|
Mobile from = e.Mobile;
|
|
Account acct = from.Account as Account;
|
|
|
|
if ( acct == null )
|
|
return;
|
|
|
|
IPAddress[] accessList = acct.LoginIPs;
|
|
|
|
if ( accessList.Length == 0 )
|
|
return;
|
|
|
|
NetState ns = from.NetState;
|
|
|
|
if ( ns == null )
|
|
return;
|
|
|
|
if ( e.Length == 0 )
|
|
{
|
|
from.SendMessage( "You must specify the new password." );
|
|
return;
|
|
}
|
|
else if ( e.Length == 1 )
|
|
{
|
|
from.SendMessage( "To prevent potential typing mistakes, you must type the password twice. Use the format:" );
|
|
from.SendMessage( "Password \"(newPassword)\" \"(repeated)\"" );
|
|
return;
|
|
}
|
|
|
|
string pass = e.GetString( 0 );
|
|
string pass2 = e.GetString( 1 );
|
|
|
|
if ( pass != pass2 )
|
|
{
|
|
from.SendMessage( "The passwords do not match." );
|
|
return;
|
|
}
|
|
|
|
bool isSafe = true;
|
|
|
|
for ( int i = 0; isSafe && i < pass.Length; ++i )
|
|
isSafe = ( pass[i] >= 0x20 && pass[i] < 0x80 );
|
|
|
|
if ( !isSafe )
|
|
{
|
|
from.SendMessage( "That is not a valid password." );
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
IPAddress ipAddress = ns.Address;
|
|
|
|
if ( Utility.IPMatchClassC( accessList[0], ipAddress ) )
|
|
{
|
|
acct.SetPassword( pass );
|
|
from.SendMessage( "The password to your account has changed." );
|
|
}
|
|
else
|
|
{
|
|
PageEntry entry = PageQueue.GetEntry( from );
|
|
|
|
if ( entry != null )
|
|
{
|
|
if ( entry.Message.StartsWith( "[Automated: Change Password]" ) )
|
|
from.SendMessage( "You already have a password change request in the help system queue." );
|
|
else
|
|
from.SendMessage( "Your IP address does not match that which created this account." );
|
|
}
|
|
else if ( PageQueue.CheckAllowedToPage( from ) )
|
|
{
|
|
from.SendMessage( "Your IP address does not match that which created this account. A page has been entered into the help system on your behalf." );
|
|
|
|
from.SendLocalizedMessage( 501234, "", 0x35 ); /* The next available Counselor/Game Master will respond as soon as possible.
|
|
* Please check your Journal for messages every few minutes.
|
|
*/
|
|
|
|
PageQueue.Enqueue( new PageEntry( from, String.Format( "[Automated: Change Password]<br>Desired password: {0}<br>Current IP address: {1}<br>Account IP address: {2}", pass, ipAddress, accessList[0] ), PageType.Account ) );
|
|
}
|
|
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
|
|
private static void EventSink_DeleteRequest( DeleteRequestEventArgs e )
|
|
{
|
|
NetState state = e.State;
|
|
int index = e.Index;
|
|
|
|
Account acct = state.Account as Account;
|
|
|
|
if ( acct == null )
|
|
{
|
|
state.Dispose();
|
|
}
|
|
else if ( index < 0 || index >= acct.Length )
|
|
{
|
|
state.Send( new DeleteResult( DeleteResultType.BadRequest ) );
|
|
state.Send( new CharacterListUpdate( acct ) );
|
|
}
|
|
else
|
|
{
|
|
Mobile m = acct[index];
|
|
|
|
if ( m == null )
|
|
{
|
|
state.Send( new DeleteResult( DeleteResultType.CharNotExist ) );
|
|
state.Send( new CharacterListUpdate( acct ) );
|
|
}
|
|
else if ( m.NetState != null )
|
|
{
|
|
state.Send( new DeleteResult( DeleteResultType.CharBeingPlayed ) );
|
|
state.Send( new CharacterListUpdate( acct ) );
|
|
}
|
|
else if ( RestrictDeletion && DateTime.Now < (m.CreationTime + DeleteDelay) )
|
|
{
|
|
state.Send( new DeleteResult( DeleteResultType.CharTooYoung ) );
|
|
state.Send( new CharacterListUpdate( acct ) );
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine( "Client: {0}: Deleting character {1} (0x{2:X})", state, index, m.Serial.Value );
|
|
|
|
acct.Comments.Add( new AccountComment( "System", String.Format( "Character #{0} {1} deleted by {2}", index + 1, m, state ) ) );
|
|
|
|
m.Delete();
|
|
state.Send( new CharacterListUpdate( acct ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool CanCreate( IPAddress ip )
|
|
{
|
|
if ( !IPTable.ContainsKey( ip ) )
|
|
return true;
|
|
|
|
return ( IPTable[ip] < MaxAccountsPerIP );
|
|
}
|
|
|
|
private static Dictionary<IPAddress, Int32> m_IPTable;
|
|
|
|
public static Dictionary<IPAddress, Int32> IPTable
|
|
{
|
|
get
|
|
{
|
|
if ( m_IPTable == null )
|
|
{
|
|
m_IPTable = new Dictionary<IPAddress, Int32>();
|
|
|
|
foreach ( Account a in Accounts.GetAccounts() )
|
|
if ( a.LoginIPs.Length > 0 )
|
|
{
|
|
IPAddress ip = a.LoginIPs[0];
|
|
|
|
if ( m_IPTable.ContainsKey( ip ) )
|
|
m_IPTable[ip]++;
|
|
else
|
|
m_IPTable[ip] = 1;
|
|
}
|
|
}
|
|
|
|
return m_IPTable;
|
|
}
|
|
}
|
|
|
|
private static Account CreateAccount( NetState state, string un, string pw )
|
|
{
|
|
if ( un.Length == 0 || pw.Length == 0 )
|
|
return null;
|
|
|
|
bool isSafe = true;
|
|
|
|
for ( int i = 0; isSafe && i < un.Length; ++i )
|
|
isSafe = ( un[i] >= 0x20 && un[i] < 0x80 );
|
|
|
|
for ( int i = 0; isSafe && i < pw.Length; ++i )
|
|
isSafe = ( pw[i] >= 0x20 && pw[i] < 0x80 );
|
|
|
|
if ( !isSafe )
|
|
return null;
|
|
|
|
if ( !CanCreate( state.Address ) )
|
|
{
|
|
Console.WriteLine( "Login: {0}: Account '{1}' not created, ip already has {2} account{3}.", state, un, MaxAccountsPerIP, MaxAccountsPerIP == 1 ? "" : "s" );
|
|
return null;
|
|
}
|
|
|
|
Console.WriteLine( "Login: {0}: Creating new account '{1}'", state, un );
|
|
|
|
Account a = new Account( un, pw );
|
|
|
|
return a;
|
|
}
|
|
|
|
public static void EventSink_AccountLogin( AccountLoginEventArgs e )
|
|
{
|
|
if ( !IPLimiter.SocketBlock && !IPLimiter.Verify( e.State.Address ) )
|
|
{
|
|
e.Accepted = false;
|
|
e.RejectReason = ALRReason.InUse;
|
|
|
|
Console.WriteLine( "Login: {0}: Past IP limit threshold", e.State );
|
|
|
|
using ( StreamWriter op = new StreamWriter( "ipLimits.log", true ) )
|
|
op.WriteLine( "{0}\tPast IP limit threshold\t{1}", e.State, DateTime.Now );
|
|
|
|
return;
|
|
}
|
|
|
|
string un = e.Username;
|
|
string pw = e.Password;
|
|
|
|
e.Accepted = false;
|
|
Account acct = Accounts.GetAccount( un ) as Account;
|
|
|
|
if ( acct == null )
|
|
{
|
|
if ( AutoAccountCreation && un.Trim().Length > 0 ) //To prevent someone from making an account of just '' or a bunch of meaningless spaces
|
|
{
|
|
e.State.Account = acct = CreateAccount( e.State, un, pw );
|
|
e.Accepted = acct == null ? false : acct.CheckAccess( e.State );
|
|
|
|
if ( !e.Accepted )
|
|
e.RejectReason = ALRReason.BadComm;
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine( "Login: {0}: Invalid username '{1}'", e.State, un );
|
|
e.RejectReason = ALRReason.Invalid;
|
|
}
|
|
}
|
|
else if ( !acct.HasAccess( e.State ) )
|
|
{
|
|
Console.WriteLine( "Login: {0}: Access denied for '{1}'", e.State, un );
|
|
e.RejectReason = ( m_LockdownLevel > AccessLevel.Player ? ALRReason.BadComm : ALRReason.BadPass );
|
|
}
|
|
else if ( !acct.CheckPassword( pw ) )
|
|
{
|
|
Console.WriteLine( "Login: {0}: Invalid password for '{1}'", e.State, un );
|
|
e.RejectReason = ALRReason.BadPass;
|
|
}
|
|
else if ( acct.Banned )
|
|
{
|
|
Console.WriteLine( "Login: {0}: Banned account '{1}'", e.State, un );
|
|
e.RejectReason = ALRReason.Blocked;
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine( "Login: {0}: Valid credentials for '{1}'", e.State, un );
|
|
e.State.Account = acct;
|
|
e.Accepted = true;
|
|
|
|
acct.LogAccess( e.State );
|
|
}
|
|
|
|
if ( !e.Accepted )
|
|
AccountAttackLimiter.RegisterInvalidAccess( e.State );
|
|
}
|
|
|
|
public static void EventSink_GameLogin( GameLoginEventArgs e )
|
|
{
|
|
if ( !IPLimiter.SocketBlock && !IPLimiter.Verify( e.State.Address ) )
|
|
{
|
|
e.Accepted = false;
|
|
|
|
Console.WriteLine( "Login: {0}: Past IP limit threshold", e.State );
|
|
|
|
using ( StreamWriter op = new StreamWriter( "ipLimits.log", true ) )
|
|
op.WriteLine( "{0}\tPast IP limit threshold\t{1}", e.State, DateTime.Now );
|
|
|
|
return;
|
|
}
|
|
|
|
string un = e.Username;
|
|
string pw = e.Password;
|
|
|
|
Account acct = Accounts.GetAccount( un ) as Account;
|
|
|
|
if ( acct == null )
|
|
{
|
|
e.Accepted = false;
|
|
}
|
|
else if ( !acct.HasAccess( e.State ) )
|
|
{
|
|
Console.WriteLine( "Login: {0}: Access denied for '{1}'", e.State, un );
|
|
e.Accepted = false;
|
|
}
|
|
else if ( !acct.CheckPassword( pw ) )
|
|
{
|
|
Console.WriteLine( "Login: {0}: Invalid password for '{1}'", e.State, un );
|
|
e.Accepted = false;
|
|
}
|
|
else if ( acct.Banned )
|
|
{
|
|
Console.WriteLine( "Login: {0}: Banned account '{1}'", e.State, un );
|
|
e.Accepted = false;
|
|
}
|
|
else
|
|
{
|
|
acct.LogAccess( e.State );
|
|
|
|
Console.WriteLine( "Login: {0}: Account '{1}' at character list", e.State, un );
|
|
e.State.Account = acct;
|
|
e.Accepted = true;
|
|
e.CityInfo = StartingCities;
|
|
}
|
|
|
|
if ( !e.Accepted )
|
|
AccountAttackLimiter.RegisterInvalidAccess( e.State );
|
|
}
|
|
}
|
|
} |