using SharpChat.Flashii; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; namespace SharpChat { public interface IBan { DateTimeOffset Expires { get; } string ToString(); } public class BannedUser : IBan { public long UserId { get; set; } public DateTimeOffset Expires { get; set; } public string Username { get; set; } public BannedUser() { } public BannedUser(FlashiiBan fb) { UserId = fb.UserId; Expires = fb.Expires; Username = fb.Username; } public override string ToString() => Username; } public class BannedIPAddress : IBan { public IPAddress Address { get; set; } public DateTimeOffset Expires { get; set; } public BannedIPAddress() { } public BannedIPAddress(FlashiiBan fb) { Address = IPAddress.Parse(fb.UserIP); Expires = fb.Expires; } public override string ToString() => Address.ToString(); } public class BanManager : IDisposable { private readonly List BanList = new(); private readonly HttpClient HttpClient; public readonly ChatContext Context; public bool IsDisposed { get; private set; } public BanManager(HttpClient httpClient, ChatContext context) { HttpClient = httpClient; Context = context; RefreshFlashiiBans().Wait(); } public void Add(ChatUser user, DateTimeOffset expires) { if(expires <= DateTimeOffset.Now) return; lock(BanList) { BannedUser ban = BanList.OfType().FirstOrDefault(x => x.UserId == user.UserId); if(ban == null) Add(new BannedUser { UserId = user.UserId, Expires = expires, Username = user.Username }); else ban.Expires = expires; } } public void Add(IPAddress addr, DateTimeOffset expires) { if(expires <= DateTimeOffset.Now) return; lock(BanList) { BannedIPAddress ban = BanList.OfType().FirstOrDefault(x => x.Address.Equals(addr)); if(ban == null) Add(new BannedIPAddress { Address = addr, Expires = expires }); else ban.Expires = expires; } } private void Add(IBan ban) { if(ban == null) return; lock(BanList) if(!BanList.Contains(ban)) BanList.Add(ban); } public void Remove(ChatUser user) { lock(BanList) BanList.RemoveAll(x => x is BannedUser ub && ub.UserId == user.UserId); } public void Remove(IPAddress addr) { lock(BanList) BanList.RemoveAll(x => x is BannedIPAddress ib && ib.Address.Equals(addr)); } public void Remove(IBan ban) { lock(BanList) BanList.Remove(ban); } public DateTimeOffset Check(ChatUser user) { if(user == null) return DateTimeOffset.MinValue; lock(BanList) return BanList.OfType().Where(x => x.UserId == user.UserId).FirstOrDefault()?.Expires ?? DateTimeOffset.MinValue; } public DateTimeOffset Check(IPAddress addr) { if(addr == null) return DateTimeOffset.MinValue; lock(BanList) return BanList.OfType().Where(x => x.Address.Equals(addr)).FirstOrDefault()?.Expires ?? DateTimeOffset.MinValue; } public BannedUser GetUser(string username) { if(username == null) return null; if(!long.TryParse(username, out long userId)) userId = 0; lock(BanList) return BanList.OfType().FirstOrDefault(x => x.Username.ToLowerInvariant() == username.ToLowerInvariant() || (userId > 0 && x.UserId == userId)); } public BannedIPAddress GetIPAddress(IPAddress addr) { lock(BanList) return BanList.OfType().FirstOrDefault(x => x.Address.Equals(addr)); } public void RemoveExpired() { lock(BanList) BanList.RemoveAll(x => x.Expires <= DateTimeOffset.Now); } public async Task RefreshFlashiiBans() { IEnumerable bans = await FlashiiBan.GetListAsync(HttpClient); if(!bans.Any()) return; lock(BanList) foreach(FlashiiBan fb in bans) { if(!BanList.OfType().Any(x => x.UserId == fb.UserId)) Add(new BannedUser(fb)); if(!BanList.OfType().Any(x => x.Address.ToString() == fb.UserIP)) Add(new BannedIPAddress(fb)); } } public IEnumerable All() { lock(BanList) return BanList.ToList(); } ~BanManager() => DoDispose(); public void Dispose() { DoDispose(); GC.SuppressFinalize(this); } private void DoDispose() { if(IsDisposed) return; IsDisposed = true; BanList.Clear(); } } }