Code style updates.

This commit is contained in:
flash 2023-02-07 16:01:56 +01:00
parent c9cc5ff23a
commit 4104e40843
42 changed files with 292 additions and 317 deletions

View file

@ -26,7 +26,9 @@ namespace SharpChat {
Username = fb.Username; Username = fb.Username;
} }
public override string ToString() => Username; public override string ToString() {
return Username;
}
} }
public class BannedIPAddress : IBan { public class BannedIPAddress : IBan {
@ -41,7 +43,9 @@ namespace SharpChat {
Expires = fb.Expires; Expires = fb.Expires;
} }
public override string ToString() => Address.ToString(); public override string ToString() {
return Address.ToString();
}
} }
public class BanManager : IDisposable { public class BanManager : IDisposable {
@ -168,8 +172,9 @@ namespace SharpChat {
return BanList.ToList(); return BanList.ToList();
} }
~BanManager() ~BanManager() {
=> DoDispose(); DoDispose();
}
public void Dispose() { public void Dispose() {
DoDispose(); DoDispose();

View file

@ -9,7 +9,7 @@ namespace SharpChat {
public class ChannelInvalidNameException : ChannelException { } public class ChannelInvalidNameException : ChannelException { }
public class ChannelManager : IDisposable { public class ChannelManager : IDisposable {
private readonly List<ChatChannel> Channels = new List<ChatChannel>(); private readonly List<ChatChannel> Channels = new();
public readonly ChatContext Context; public readonly ChatContext Context;
@ -23,43 +23,43 @@ namespace SharpChat {
public ChatChannel DefaultChannel { public ChatChannel DefaultChannel {
get { get {
if (_DefaultChannel == null) if(_DefaultChannel == null)
_DefaultChannel = Channels.FirstOrDefault(); _DefaultChannel = Channels.FirstOrDefault();
return _DefaultChannel; return _DefaultChannel;
} }
set { set {
if (value == null) if(value == null)
return; return;
if (Channels.Contains(value)) if(Channels.Contains(value))
_DefaultChannel = value; _DefaultChannel = value;
} }
} }
public void Add(ChatChannel channel) { public void Add(ChatChannel channel) {
if (channel == null) if(channel == null)
throw new ArgumentNullException(nameof(channel)); throw new ArgumentNullException(nameof(channel));
if (!channel.Name.All(c => char.IsLetter(c) || char.IsNumber(c) || c == '-')) if(!channel.Name.All(c => char.IsLetter(c) || char.IsNumber(c) || c == '-'))
throw new ChannelInvalidNameException(); throw new ChannelInvalidNameException();
if (Get(channel.Name) != null) if(Get(channel.Name) != null)
throw new ChannelExistException(); throw new ChannelExistException();
// Add channel to the listing // Add channel to the listing
Channels.Add(channel); Channels.Add(channel);
// Set as default if there's none yet // Set as default if there's none yet
if (_DefaultChannel == null) if(_DefaultChannel == null)
_DefaultChannel = channel; _DefaultChannel = channel;
// Broadcast creation of channel // Broadcast creation of channel
foreach (ChatUser user in Context.Users.OfHierarchy(channel.Rank)) foreach(ChatUser user in Context.Users.OfHierarchy(channel.Rank))
user.Send(new ChannelCreatePacket(channel)); user.Send(new ChannelCreatePacket(channel));
} }
public void Remove(ChatChannel channel) { public void Remove(ChatChannel channel) {
if (channel == null || channel == DefaultChannel) if(channel == null || channel == DefaultChannel)
return; return;
// Remove channel from the listing // Remove channel from the listing
@ -67,94 +67,94 @@ namespace SharpChat {
// Move all users back to the main channel // Move all users back to the main channel
// TODO: Replace this with a kick. SCv2 supports being in 0 channels, SCv1 should force the user back to DefaultChannel. // TODO: Replace this with a kick. SCv2 supports being in 0 channels, SCv1 should force the user back to DefaultChannel.
foreach (ChatUser user in channel.GetUsers()) { foreach(ChatUser user in channel.GetUsers()) {
Context.SwitchChannel(user, DefaultChannel, string.Empty); Context.SwitchChannel(user, DefaultChannel, string.Empty);
} }
// Broadcast deletion of channel // Broadcast deletion of channel
foreach (ChatUser user in Context.Users.OfHierarchy(channel.Rank)) foreach(ChatUser user in Context.Users.OfHierarchy(channel.Rank))
user.Send(new ChannelDeletePacket(channel)); user.Send(new ChannelDeletePacket(channel));
} }
public bool Contains(ChatChannel chan) { public bool Contains(ChatChannel chan) {
if (chan == null) if(chan == null)
return false; return false;
lock (Channels) lock(Channels)
return Channels.Contains(chan) || Channels.Any(c => c.Name.ToLowerInvariant() == chan.Name.ToLowerInvariant()); return Channels.Contains(chan) || Channels.Any(c => c.Name.ToLowerInvariant() == chan.Name.ToLowerInvariant());
} }
public void Update(ChatChannel channel, string name = null, bool? temporary = null, int? hierarchy = null, string password = null) { public void Update(ChatChannel channel, string name = null, bool? temporary = null, int? hierarchy = null, string password = null) {
if (channel == null) if(channel == null)
throw new ArgumentNullException(nameof(channel)); throw new ArgumentNullException(nameof(channel));
if (!Channels.Contains(channel)) if(!Channels.Contains(channel))
throw new ArgumentException(@"Provided channel is not registered with this manager.", nameof(channel)); throw new ArgumentException(@"Provided channel is not registered with this manager.", nameof(channel));
string prevName = channel.Name; string prevName = channel.Name;
int prevHierarchy = channel.Rank; int prevHierarchy = channel.Rank;
bool nameUpdated = !string.IsNullOrWhiteSpace(name) && name != prevName; bool nameUpdated = !string.IsNullOrWhiteSpace(name) && name != prevName;
if (nameUpdated) { if(nameUpdated) {
if (!name.All(c => char.IsLetter(c) || char.IsNumber(c) || c == '-')) if(!name.All(c => char.IsLetter(c) || char.IsNumber(c) || c == '-'))
throw new ChannelInvalidNameException(); throw new ChannelInvalidNameException();
if (Get(name) != null) if(Get(name) != null)
throw new ChannelExistException(); throw new ChannelExistException();
channel.Name = name; channel.Name = name;
} }
if (temporary.HasValue) if(temporary.HasValue)
channel.IsTemporary = temporary.Value; channel.IsTemporary = temporary.Value;
if (hierarchy.HasValue) if(hierarchy.HasValue)
channel.Rank = hierarchy.Value; channel.Rank = hierarchy.Value;
if (password != null) if(password != null)
channel.Password = password; channel.Password = password;
// Users that no longer have access to the channel/gained access to the channel by the hierarchy change should receive delete and create packets respectively // Users that no longer have access to the channel/gained access to the channel by the hierarchy change should receive delete and create packets respectively
foreach (ChatUser user in Context.Users.OfHierarchy(channel.Rank)) { foreach(ChatUser user in Context.Users.OfHierarchy(channel.Rank)) {
user.Send(new ChannelUpdatePacket(prevName, channel)); user.Send(new ChannelUpdatePacket(prevName, channel));
if (nameUpdated) if(nameUpdated)
user.ForceChannel(); user.ForceChannel();
} }
} }
public ChatChannel Get(string name) { public ChatChannel Get(string name) {
if (string.IsNullOrWhiteSpace(name)) if(string.IsNullOrWhiteSpace(name))
return null; return null;
return Channels.FirstOrDefault(x => x.Name.ToLowerInvariant() == name.ToLowerInvariant()); return Channels.FirstOrDefault(x => x.Name.ToLowerInvariant() == name.ToLowerInvariant());
} }
public IEnumerable<ChatChannel> GetUser(ChatUser user) { public IEnumerable<ChatChannel> GetUser(ChatUser user) {
if (user == null) if(user == null)
return null; return null;
return Channels.Where(x => x.HasUser(user)); return Channels.Where(x => x.HasUser(user));
} }
public IEnumerable<ChatChannel> OfHierarchy(int hierarchy) { public IEnumerable<ChatChannel> OfHierarchy(int hierarchy) {
lock (Channels) lock(Channels)
return Channels.Where(c => c.Rank <= hierarchy).ToList(); return Channels.Where(c => c.Rank <= hierarchy).ToList();
} }
~ChannelManager() ~ChannelManager() {
=> Dispose(false); DoDispose();
}
public void Dispose() public void Dispose() {
=> Dispose(true); DoDispose();
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing) { private void DoDispose() {
if (IsDisposed) if(IsDisposed)
return; return;
IsDisposed = true; IsDisposed = true;
Channels.Clear(); Channels.Clear();
if (disposing)
GC.SuppressFinalize(this);
} }
} }
} }

View file

@ -10,8 +10,7 @@ namespace SharpChat {
public int Rank { get; set; } = 0; public int Rank { get; set; } = 0;
public ChatUser Owner { get; set; } = null; public ChatUser Owner { get; set; } = null;
private List<ChatUser> Users { get; } = new List<ChatUser>(); private List<ChatUser> Users { get; } = new();
private List<ChatChannelTyping> Typing { get; } = new List<ChatChannelTyping>();
public bool HasPassword public bool HasPassword
=> !string.IsNullOrWhiteSpace(Password); => !string.IsNullOrWhiteSpace(Password);
@ -26,73 +25,51 @@ namespace SharpChat {
} }
public bool HasUser(ChatUser user) { public bool HasUser(ChatUser user) {
lock (Users) lock(Users)
return Users.Contains(user); return Users.Contains(user);
} }
public void UserJoin(ChatUser user) { public void UserJoin(ChatUser user) {
if (!user.InChannel(this)) { if(!user.InChannel(this)) {
// Remove this, a different means for this should be established for V1 compat. // Remove this, a different means for this should be established for V1 compat.
user.Channel?.UserLeave(user); user.Channel?.UserLeave(user);
user.JoinChannel(this); user.JoinChannel(this);
} }
lock (Users) { lock(Users) {
if (!HasUser(user)) if(!HasUser(user))
Users.Add(user); Users.Add(user);
} }
} }
public void UserLeave(ChatUser user) { public void UserLeave(ChatUser user) {
lock (Users) lock(Users)
Users.Remove(user); Users.Remove(user);
if (user.InChannel(this)) if(user.InChannel(this))
user.LeaveChannel(this); user.LeaveChannel(this);
} }
public void Send(IServerPacket packet) { public void Send(IServerPacket packet) {
lock (Users) { lock(Users) {
foreach (ChatUser user in Users) foreach(ChatUser user in Users)
user.Send(packet); user.Send(packet);
} }
} }
public IEnumerable<ChatUser> GetUsers(IEnumerable<ChatUser> exclude = null) { public IEnumerable<ChatUser> GetUsers(IEnumerable<ChatUser> exclude = null) {
lock (Users) { lock(Users) {
IEnumerable<ChatUser> users = Users.OrderByDescending(x => x.Rank); IEnumerable<ChatUser> users = Users.OrderByDescending(x => x.Rank);
if (exclude != null) if(exclude != null)
users = users.Except(exclude); users = users.Except(exclude);
return users.ToList(); return users.ToList();
} }
} }
public bool IsTyping(ChatUser user) {
if(user == null)
return false;
lock(Typing)
return Typing.Any(x => x.User == user && !x.HasExpired);
}
public bool CanType(ChatUser user) {
if(user == null || !HasUser(user))
return false;
return !IsTyping(user);
}
public ChatChannelTyping RegisterTyping(ChatUser user) {
if(user == null || !HasUser(user))
return null;
ChatChannelTyping typing = new ChatChannelTyping(user);
lock(Typing) {
Typing.RemoveAll(x => x.HasExpired);
Typing.Add(typing);
}
return typing;
}
public string Pack() { public string Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append(Name); sb.Append(Name);
sb.Append('\t'); sb.Append('\t');

View file

@ -1,18 +0,0 @@
using System;
namespace SharpChat {
public class ChatChannelTyping {
public static TimeSpan Lifetime { get; } = TimeSpan.FromSeconds(5);
public ChatUser User { get; }
public DateTimeOffset Started { get; }
public bool HasExpired
=> DateTimeOffset.Now - Started > Lifetime;
public ChatChannelTyping(ChatUser user) {
User = user ?? throw new ArgumentNullException(nameof(user));
Started = DateTimeOffset.Now;
}
}
}

View file

@ -15,7 +15,7 @@
public bool Inherit { public bool Inherit {
get => (Raw & INHERIT) > 0; get => (Raw & INHERIT) > 0;
set { set {
if (value) if(value)
Raw |= INHERIT; Raw |= INHERIT;
else else
Raw &= ~INHERIT; Raw &= ~INHERIT;
@ -47,7 +47,7 @@
} }
public override string ToString() { public override string ToString() {
if (Inherit) if(Inherit)
return @"inherit"; return @"inherit";
return string.Format(@"#{0:X6}", Raw); return string.Format(@"#{0:X6}", Raw);
} }

View file

@ -28,10 +28,10 @@ namespace SharpChat {
public ChatContext(HttpClient httpClient, SockChatServer server) { public ChatContext(HttpClient httpClient, SockChatServer server) {
HttpClient = httpClient; HttpClient = httpClient;
Server = server; Server = server;
Bans = new BanManager(httpClient, this); Bans = new(httpClient, this);
Users = new UserManager(this); Users = new(this);
Channels = new ChannelManager(this); Channels = new(this);
Events = new ChatEventManager(this); Events = new(this);
BumpTimer = new Timer(e => FlashiiBump.SubmitAsync(HttpClient, Users.WithActiveConnections()).Wait(), null, TimeSpan.Zero, TimeSpan.FromMinutes(1)); BumpTimer = new Timer(e => FlashiiBump.SubmitAsync(HttpClient, Users.WithActiveConnections()).Wait(), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
} }
@ -43,10 +43,10 @@ namespace SharpChat {
} }
public void BanUser(ChatUser user, DateTimeOffset? until = null, bool banIPs = false, UserDisconnectReason reason = UserDisconnectReason.Kicked) { public void BanUser(ChatUser user, DateTimeOffset? until = null, bool banIPs = false, UserDisconnectReason reason = UserDisconnectReason.Kicked) {
if (until.HasValue && until.Value <= DateTimeOffset.UtcNow) if(until.HasValue && until.Value <= DateTimeOffset.UtcNow)
until = null; until = null;
if (until.HasValue) { if(until.HasValue) {
user.Send(new ForceDisconnectPacket(ForceDisconnectReason.Banned, until.Value)); user.Send(new ForceDisconnectPacket(ForceDisconnectReason.Banned, until.Value));
lock(BansAccess) { lock(BansAccess) {
@ -64,7 +64,7 @@ namespace SharpChat {
} }
public void HandleJoin(ChatUser user, ChatChannel chan, ChatUserSession sess) { public void HandleJoin(ChatUser user, ChatChannel chan, ChatUserSession sess) {
if (!chan.HasUser(user)) { if(!chan.HasUser(user)) {
chan.Send(new UserConnectPacket(DateTimeOffset.Now, user)); chan.Send(new UserConnectPacket(DateTimeOffset.Now, user));
Events.Add(new UserConnectEvent(DateTimeOffset.Now, user, chan)); Events.Add(new UserConnectEvent(DateTimeOffset.Now, user, chan));
} }
@ -79,24 +79,24 @@ namespace SharpChat {
sess.Send(new ContextChannelsPacket(Channels.OfHierarchy(user.Rank))); sess.Send(new ContextChannelsPacket(Channels.OfHierarchy(user.Rank)));
if (!chan.HasUser(user)) if(!chan.HasUser(user))
chan.UserJoin(user); chan.UserJoin(user);
if (!Users.Contains(user)) if(!Users.Contains(user))
Users.Add(user); Users.Add(user);
} }
public void UserLeave(ChatChannel chan, ChatUser user, UserDisconnectReason reason = UserDisconnectReason.Leave) { public void UserLeave(ChatChannel chan, ChatUser user, UserDisconnectReason reason = UserDisconnectReason.Leave) {
user.Status = ChatUserStatus.Offline; user.Status = ChatUserStatus.Offline;
if (chan == null) { if(chan == null) {
foreach(ChatChannel channel in user.GetChannels()) { foreach(ChatChannel channel in user.GetChannels()) {
UserLeave(channel, user, reason); UserLeave(channel, user, reason);
} }
return; return;
} }
if (chan.IsTemporary && chan.Owner == user) if(chan.IsTemporary && chan.Owner == user)
Channels.Remove(chan); Channels.Remove(chan);
chan.UserLeave(user); chan.UserLeave(user);
@ -105,20 +105,20 @@ namespace SharpChat {
} }
public void SwitchChannel(ChatUser user, ChatChannel chan, string password) { public void SwitchChannel(ChatUser user, ChatChannel chan, string password) {
if (user.CurrentChannel == chan) { if(user.CurrentChannel == chan) {
//user.Send(true, @"samechan", chan.Name); //user.Send(true, @"samechan", chan.Name);
user.ForceChannel(); user.ForceChannel();
return; return;
} }
if (!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.Owner != user) { if(!user.Can(ChatUserPermissions.JoinAnyChannel) && chan.Owner != user) {
if (chan.Rank > user.Rank) { if(chan.Rank > user.Rank) {
user.Send(new LegacyCommandResponse(LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name)); user.Send(new LegacyCommandResponse(LCR.CHANNEL_INSUFFICIENT_HIERARCHY, true, chan.Name));
user.ForceChannel(); user.ForceChannel();
return; return;
} }
if (chan.Password != password) { if(chan.Password != password) {
user.Send(new LegacyCommandResponse(LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name)); user.Send(new LegacyCommandResponse(LCR.CHANNEL_INVALID_PASSWORD, true, chan.Name));
user.ForceChannel(); user.ForceChannel();
return; return;
@ -129,7 +129,7 @@ namespace SharpChat {
} }
public void ForceChannelSwitch(ChatUser user, ChatChannel chan) { public void ForceChannelSwitch(ChatUser user, ChatChannel chan) {
if (!Channels.Contains(chan)) if(!Channels.Contains(chan))
return; return;
ChatChannel oldChan = user.CurrentChannel; ChatChannel oldChan = user.CurrentChannel;
@ -144,20 +144,20 @@ namespace SharpChat {
IEnumerable<IChatEvent> msgs = Events.GetTargetLog(chan); IEnumerable<IChatEvent> msgs = Events.GetTargetLog(chan);
foreach (IChatEvent msg in msgs) foreach(IChatEvent msg in msgs)
user.Send(new ContextMessagePacket(msg)); user.Send(new ContextMessagePacket(msg));
user.ForceChannel(chan); user.ForceChannel(chan);
oldChan.UserLeave(user); oldChan.UserLeave(user);
chan.UserJoin(user); chan.UserJoin(user);
if (oldChan.IsTemporary && oldChan.Owner == user) if(oldChan.IsTemporary && oldChan.Owner == user)
Channels.Remove(oldChan); Channels.Remove(oldChan);
} }
public void CheckPings() { public void CheckPings() {
lock(Users) lock(Users)
foreach (ChatUser user in Users.All()) { foreach(ChatUser user in Users.All()) {
IEnumerable<ChatUserSession> timedOut = user.GetDeadSessions(); IEnumerable<ChatUserSession> timedOut = user.GetDeadSessions();
foreach(ChatUserSession sess in timedOut) { foreach(ChatUserSession sess in timedOut) {
@ -172,7 +172,7 @@ namespace SharpChat {
} }
public void Send(IServerPacket packet) { public void Send(IServerPacket packet) {
foreach (ChatUser user in Users.All()) foreach(ChatUser user in Users.All())
user.Send(packet); user.Send(packet);
} }
@ -186,7 +186,7 @@ namespace SharpChat {
} }
private void DoDispose() { private void DoDispose() {
if (IsDisposed) if(IsDisposed)
return; return;
IsDisposed = true; IsDisposed = true;

View file

@ -15,12 +15,12 @@ namespace SharpChat {
public ChatEventManager(ChatContext context) { public ChatEventManager(ChatContext context) {
Context = context; Context = context;
if (!Database.HasDatabase) if(!Database.HasDatabase)
Events = new List<IChatEvent>(); Events = new();
} }
public void Add(IChatEvent evt) { public void Add(IChatEvent evt) {
if (evt == null) if(evt == null)
throw new ArgumentNullException(nameof(evt)); throw new ArgumentNullException(nameof(evt));
if(Events != null) if(Events != null)
@ -32,39 +32,39 @@ namespace SharpChat {
} }
public void Remove(IChatEvent evt) { public void Remove(IChatEvent evt) {
if (evt == null) if(evt == null)
return; return;
if (Events != null) if(Events != null)
lock (Events) lock(Events)
Events.Remove(evt); Events.Remove(evt);
if (Database.HasDatabase) if(Database.HasDatabase)
Database.DeleteEvent(evt); Database.DeleteEvent(evt);
Context.Send(new ChatMessageDeletePacket(evt.SequenceId)); Context.Send(new ChatMessageDeletePacket(evt.SequenceId));
} }
public IChatEvent Get(long seqId) { public IChatEvent Get(long seqId) {
if (seqId < 1) if(seqId < 1)
return null; return null;
if (Database.HasDatabase) if(Database.HasDatabase)
return Database.GetEvent(seqId); return Database.GetEvent(seqId);
if (Events != null) if(Events != null)
lock (Events) lock(Events)
return Events.FirstOrDefault(e => e.SequenceId == seqId); return Events.FirstOrDefault(e => e.SequenceId == seqId);
return null; return null;
} }
public IEnumerable<IChatEvent> GetTargetLog(IPacketTarget target, int amount = 20, int offset = 0) { public IEnumerable<IChatEvent> GetTargetLog(IPacketTarget target, int amount = 20, int offset = 0) {
if (Database.HasDatabase) if(Database.HasDatabase)
return Database.GetEvents(target, amount, offset).Reverse(); return Database.GetEvents(target, amount, offset).Reverse();
if (Events != null) if(Events != null)
lock (Events) { lock(Events) {
IEnumerable<IChatEvent> subset = Events.Where(e => e.Target == target || e.Target == null); IEnumerable<IChatEvent> subset = Events.Where(e => e.Target == target || e.Target == null);
int start = subset.Count() - offset - amount; int start = subset.Count() - offset - amount;
@ -80,21 +80,21 @@ namespace SharpChat {
return Enumerable.Empty<IChatEvent>(); return Enumerable.Empty<IChatEvent>();
} }
~ChatEventManager() ~ChatEventManager() {
=> Dispose(false); DoDispose();
}
public void Dispose() public void Dispose() {
=> Dispose(true); DoDispose();
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing) { private void DoDispose() {
if (IsDisposed) if(IsDisposed)
return; return;
IsDisposed = true; IsDisposed = true;
Events?.Clear(); Events?.Clear();
if (disposing)
GC.SuppressFinalize(this);
} }
} }
} }

View file

@ -13,16 +13,16 @@ namespace SharpChat {
private const int FLOOD_PROTECTION_AMOUNT = 30; private const int FLOOD_PROTECTION_AMOUNT = 30;
private const int FLOOD_PROTECTION_THRESHOLD = 10; private const int FLOOD_PROTECTION_THRESHOLD = 10;
private readonly Queue<DateTimeOffset> TimePoints = new Queue<DateTimeOffset>(); private readonly Queue<DateTimeOffset> TimePoints = new();
public ChatRateLimitState State { public ChatRateLimitState State {
get { get {
lock (TimePoints) { lock(TimePoints) {
if (TimePoints.Count == FLOOD_PROTECTION_AMOUNT) { if(TimePoints.Count == FLOOD_PROTECTION_AMOUNT) {
if ((TimePoints.Last() - TimePoints.First()).TotalSeconds <= FLOOD_PROTECTION_THRESHOLD) if((TimePoints.Last() - TimePoints.First()).TotalSeconds <= FLOOD_PROTECTION_THRESHOLD)
return ChatRateLimitState.Kick; return ChatRateLimitState.Kick;
if ((TimePoints.Last() - TimePoints.Skip(5).First()).TotalSeconds <= FLOOD_PROTECTION_THRESHOLD) if((TimePoints.Last() - TimePoints.Skip(5).First()).TotalSeconds <= FLOOD_PROTECTION_THRESHOLD)
return ChatRateLimitState.Warning; return ChatRateLimitState.Warning;
} }
@ -32,11 +32,11 @@ namespace SharpChat {
} }
public void AddTimePoint(DateTimeOffset? dto = null) { public void AddTimePoint(DateTimeOffset? dto = null) {
if (!dto.HasValue) if(!dto.HasValue)
dto = DateTimeOffset.Now; dto = DateTimeOffset.Now;
lock (TimePoints) { lock(TimePoints) {
if (TimePoints.Count >= FLOOD_PROTECTION_AMOUNT) if(TimePoints.Count >= FLOOD_PROTECTION_AMOUNT)
TimePoints.Dequeue(); TimePoints.Dequeue();
TimePoints.Enqueue(dto.Value); TimePoints.Enqueue(dto.Value);

View file

@ -23,15 +23,16 @@ namespace SharpChat {
public bool HasFloodProtection public bool HasFloodProtection
=> Rank < RANK_NO_FLOOD; => Rank < RANK_NO_FLOOD;
public bool Equals([AllowNull] BasicUser other) public bool Equals([AllowNull] BasicUser other) {
=> UserId == other.UserId; return UserId == other.UserId;
}
public string DisplayName { public string DisplayName {
get { get {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
if(Status == ChatUserStatus.Away) if(Status == ChatUserStatus.Away)
sb.AppendFormat(@"&lt;{0}&gt;_", StatusMessage.Substring(0, Math.Min(StatusMessage.Length, 5)).ToUpperInvariant()); sb.AppendFormat(@"&lt;{0}&gt;_", StatusMessage[..Math.Min(StatusMessage.Length, 5)].ToUpperInvariant());
if(string.IsNullOrWhiteSpace(Nickname)) if(string.IsNullOrWhiteSpace(Nickname))
sb.Append(Username); sb.Append(Username);
@ -50,7 +51,7 @@ namespace SharpChat {
} }
public string Pack() { public string Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append(UserId); sb.Append(UserId);
sb.Append('\t'); sb.Append('\t');
@ -61,7 +62,7 @@ namespace SharpChat {
sb.Append(Rank); sb.Append(Rank);
sb.Append(' '); sb.Append(' ');
sb.Append(Can(ChatUserPermissions.KickUser) ? '1' : '0'); sb.Append(Can(ChatUserPermissions.KickUser) ? '1' : '0');
sb.Append(@" 0 "); sb.Append(@" 0 "); // view logs
sb.Append(Can(ChatUserPermissions.SetOwnNickname) ? '1' : '0'); sb.Append(Can(ChatUserPermissions.SetOwnNickname) ? '1' : '0');
sb.Append(' '); sb.Append(' ');
sb.Append(Can(ChatUserPermissions.CreateChannel | ChatUserPermissions.SetChannelPermanent, true) ? 2 : ( sb.Append(Can(ChatUserPermissions.CreateChannel | ChatUserPermissions.SetChannelPermanent, true) ? 2 : (
@ -75,10 +76,10 @@ namespace SharpChat {
public class ChatUser : BasicUser, IPacketTarget { public class ChatUser : BasicUser, IPacketTarget {
public DateTimeOffset SilencedUntil { get; set; } public DateTimeOffset SilencedUntil { get; set; }
private readonly List<ChatUserSession> Sessions = new List<ChatUserSession>(); private readonly List<ChatUserSession> Sessions = new();
private readonly List<ChatChannel> Channels = new List<ChatChannel>(); private readonly List<ChatChannel> Channels = new();
public readonly ChatRateLimiter RateLimiter = new ChatRateLimiter(); public readonly ChatRateLimiter RateLimiter = new();
public string TargetName => @"@log"; public string TargetName => @"@log";
@ -104,7 +105,7 @@ namespace SharpChat {
public int SessionCount { public int SessionCount {
get { get {
lock (Sessions) lock(Sessions)
return Sessions.Where(c => !c.HasTimedOut && !c.IsDisposed).Count(); return Sessions.Where(c => !c.HasTimedOut && !c.IsDisposed).Count();
} }
} }
@ -127,33 +128,34 @@ namespace SharpChat {
public void ApplyAuth(FlashiiAuth auth, bool invalidateRestrictions = false) { public void ApplyAuth(FlashiiAuth auth, bool invalidateRestrictions = false) {
Username = auth.Username; Username = auth.Username;
if (Status == ChatUserStatus.Offline) if(Status == ChatUserStatus.Offline)
Status = ChatUserStatus.Online; Status = ChatUserStatus.Online;
Colour = new ChatColour(auth.ColourRaw); Colour = new(auth.ColourRaw);
Rank = auth.Rank; Rank = auth.Rank;
Permissions = auth.Permissions; Permissions = auth.Permissions;
if (invalidateRestrictions || !IsSilenced) if(invalidateRestrictions || !IsSilenced)
SilencedUntil = auth.SilencedUntil; SilencedUntil = auth.SilencedUntil;
} }
public void Send(IServerPacket packet) { public void Send(IServerPacket packet) {
lock(Sessions) lock(Sessions)
foreach (ChatUserSession conn in Sessions) foreach(ChatUserSession conn in Sessions)
conn.Send(packet); conn.Send(packet);
} }
public void Close() { public void Close() {
lock (Sessions) { lock(Sessions) {
foreach (ChatUserSession conn in Sessions) foreach(ChatUserSession conn in Sessions)
conn.Dispose(); conn.Dispose();
Sessions.Clear(); Sessions.Clear();
} }
} }
public void ForceChannel(ChatChannel chan = null) public void ForceChannel(ChatChannel chan = null) {
=> Send(new UserChannelForceJoinPacket(chan ?? CurrentChannel)); Send(new UserChannelForceJoinPacket(chan ?? CurrentChannel));
}
public void FocusChannel(ChatChannel chan) { public void FocusChannel(ChatChannel chan) {
lock(Channels) { lock(Channels) {
@ -163,12 +165,12 @@ namespace SharpChat {
} }
public bool InChannel(ChatChannel chan) { public bool InChannel(ChatChannel chan) {
lock (Channels) lock(Channels)
return Channels.Contains(chan); return Channels.Contains(chan);
} }
public void JoinChannel(ChatChannel chan) { public void JoinChannel(ChatChannel chan) {
lock (Channels) { lock(Channels) {
if(!InChannel(chan)) { if(!InChannel(chan)) {
Channels.Add(chan); Channels.Add(chan);
CurrentChannel = chan; CurrentChannel = chan;
@ -184,21 +186,21 @@ namespace SharpChat {
} }
public IEnumerable<ChatChannel> GetChannels() { public IEnumerable<ChatChannel> GetChannels() {
lock (Channels) lock(Channels)
return Channels.ToList(); return Channels.ToList();
} }
public void AddSession(ChatUserSession sess) { public void AddSession(ChatUserSession sess) {
if (sess == null) if(sess == null)
return; return;
sess.User = this; sess.User = this;
lock (Sessions) lock(Sessions)
Sessions.Add(sess); Sessions.Add(sess);
} }
public void RemoveSession(ChatUserSession sess) { public void RemoveSession(ChatUserSession sess) {
if (sess == null) if(sess == null)
return; return;
if(!sess.IsDisposed) // this could be possible if(!sess.IsDisposed) // this could be possible
sess.User = null; sess.User = null;
@ -208,7 +210,7 @@ namespace SharpChat {
} }
public IEnumerable<ChatUserSession> GetDeadSessions() { public IEnumerable<ChatUserSession> GetDeadSessions() {
lock (Sessions) lock(Sessions)
return Sessions.Where(x => x.HasTimedOut || x.IsDisposed).ToList(); return Sessions.Where(x => x.HasTimedOut || x.IsDisposed).ToList();
} }
} }

View file

@ -20,7 +20,7 @@ namespace SharpChat {
public DateTimeOffset LastPing { get; set; } = DateTimeOffset.MinValue; public DateTimeOffset LastPing { get; set; } = DateTimeOffset.MinValue;
public ChatUser User { get; set; } public ChatUser User { get; set; }
private static int CloseCode { get; set; } = 1000; private int CloseCode { get; set; } = 1000;
public string TargetName => @"@log"; public string TargetName => @"@log";
@ -65,30 +65,32 @@ namespace SharpChat {
Connection.Send(line); Connection.Send(line);
} }
public void BumpPing() public void BumpPing() {
=> LastPing = DateTimeOffset.Now; LastPing = DateTimeOffset.Now;
}
public bool HasTimedOut public bool HasTimedOut
=> DateTimeOffset.Now - LastPing > SessionTimeOut; => DateTimeOffset.Now - LastPing > SessionTimeOut;
public void PrepareForRestart() public void PrepareForRestart() {
=> CloseCode = 1012; CloseCode = 1012;
}
public void Dispose() ~ChatUserSession() {
=> Dispose(true); DoDispose();
}
~ChatUserSession() public void Dispose() {
=> Dispose(false); DoDispose();
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing) { private void DoDispose() {
if(IsDisposed) if(IsDisposed)
return; return;
IsDisposed = true; IsDisposed = true;
Connection.Close(CloseCode); Connection.Close(CloseCode);
if(disposing)
GC.SuppressFinalize(this);
} }
} }
} }

View file

@ -18,7 +18,7 @@ namespace SharpChat.Commands {
else { else {
statusText = statusText.Trim(); statusText = statusText.Trim();
if(statusText.Length > MAX_LENGTH) if(statusText.Length > MAX_LENGTH)
statusText = statusText.Substring(0, MAX_LENGTH).Trim(); statusText = statusText[..MAX_LENGTH].Trim();
} }
context.User.Status = ChatUserStatus.Away; context.User.Status = ChatUserStatus.Away;

View file

@ -17,7 +17,7 @@ namespace SharpChat {
if(!File.Exists(@"mariadb.txt")) if(!File.Exists(@"mariadb.txt"))
return; return;
string[] config = File.ReadAllLines(@"mariadb.txt"); string[] config = File.ReadAllLines(@"mariadb.txt");
if (config.Length < 4) if(config.Length < 4)
return; return;
Init(config[0], config[1], config[2], config[3]); Init(config[0], config[1], config[2], config[3]);
} }
@ -43,27 +43,27 @@ namespace SharpChat {
} }
private static MySqlConnection GetConnection() { private static MySqlConnection GetConnection() {
if (!HasDatabase) if(!HasDatabase)
return null; return null;
MySqlConnection conn = new MySqlConnection(ConnectionString); MySqlConnection conn = new(ConnectionString);
conn.Open(); conn.Open();
return conn; return conn;
} }
private static int RunCommand(string command, params MySqlParameter[] parameters) { private static int RunCommand(string command, params MySqlParameter[] parameters) {
if (!HasDatabase) if(!HasDatabase)
return 0; return 0;
try { try {
using MySqlConnection conn = GetConnection(); using MySqlConnection conn = GetConnection();
using MySqlCommand cmd = conn.CreateCommand(); using MySqlCommand cmd = conn.CreateCommand();
if (parameters?.Length > 0) if(parameters?.Length > 0)
cmd.Parameters.AddRange(parameters); cmd.Parameters.AddRange(parameters);
cmd.CommandText = command; cmd.CommandText = command;
return cmd.ExecuteNonQuery(); return cmd.ExecuteNonQuery();
} catch (MySqlException ex) { } catch(MySqlException ex) {
Logger.Write(ex); Logger.Write(ex);
} }
@ -71,13 +71,13 @@ namespace SharpChat {
} }
private static MySqlDataReader RunQuery(string command, params MySqlParameter[] parameters) { private static MySqlDataReader RunQuery(string command, params MySqlParameter[] parameters) {
if (!HasDatabase) if(!HasDatabase)
return null; return null;
try { try {
MySqlConnection conn = GetConnection(); MySqlConnection conn = GetConnection();
MySqlCommand cmd = conn.CreateCommand(); MySqlCommand cmd = conn.CreateCommand();
if (parameters?.Length > 0) if(parameters?.Length > 0)
cmd.Parameters.AddRange(parameters); cmd.Parameters.AddRange(parameters);
cmd.CommandText = command; cmd.CommandText = command;
return cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection); return cmd.ExecuteReader(System.Data.CommandBehavior.CloseConnection);
@ -89,13 +89,13 @@ namespace SharpChat {
} }
private static object RunQueryValue(string command, params MySqlParameter[] parameters) { private static object RunQueryValue(string command, params MySqlParameter[] parameters) {
if (!HasDatabase) if(!HasDatabase)
return null; return null;
try { try {
using MySqlConnection conn = GetConnection(); using MySqlConnection conn = GetConnection();
using MySqlCommand cmd = conn.CreateCommand(); using MySqlCommand cmd = conn.CreateCommand();
if (parameters?.Length > 0) if(parameters?.Length > 0)
cmd.Parameters.AddRange(parameters); cmd.Parameters.AddRange(parameters);
cmd.CommandText = command; cmd.CommandText = command;
cmd.Prepare(); cmd.Prepare();
@ -111,7 +111,7 @@ namespace SharpChat {
private static int IdCounter = 0; private static int IdCounter = 0;
public static long GenerateId() { public static long GenerateId() {
if (IdCounter > 200) if(IdCounter > 200)
IdCounter = 0; IdCounter = 0;
long id = 0; long id = 0;
@ -160,7 +160,7 @@ namespace SharpChat {
evt.Flags = (ChatMessageFlags)reader.GetByte(@"event_flags"); evt.Flags = (ChatMessageFlags)reader.GetByte(@"event_flags");
evt.DateTime = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32(@"event_created")); evt.DateTime = DateTimeOffset.FromUnixTimeSeconds(reader.GetInt32(@"event_created"));
if (!reader.IsDBNull(reader.GetOrdinal(@"event_sender"))) { if(!reader.IsDBNull(reader.GetOrdinal(@"event_sender"))) {
evt.Sender = new BasicUser { evt.Sender = new BasicUser {
UserId = reader.GetInt64(@"event_sender"), UserId = reader.GetInt64(@"event_sender"),
Username = reader.GetString(@"event_sender_name"), Username = reader.GetString(@"event_sender_name"),
@ -175,7 +175,7 @@ namespace SharpChat {
} }
public static IEnumerable<IChatEvent> GetEvents(IPacketTarget target, int amount, int offset) { public static IEnumerable<IChatEvent> GetEvents(IPacketTarget target, int amount, int offset) {
List<IChatEvent> events = new List<IChatEvent>(); List<IChatEvent> events = new();
try { try {
using MySqlDataReader reader = RunQuery( using MySqlDataReader reader = RunQuery(
@ -192,9 +192,9 @@ namespace SharpChat {
new MySqlParameter(@"offset", offset) new MySqlParameter(@"offset", offset)
); );
while (reader.Read()) { while(reader.Read()) {
IChatEvent evt = ReadEvent(reader, target); IChatEvent evt = ReadEvent(reader, target);
if (evt != null) if(evt != null)
events.Add(evt); events.Add(evt);
} }
} catch(MySqlException ex) { } catch(MySqlException ex) {
@ -215,9 +215,9 @@ namespace SharpChat {
new MySqlParameter(@"id", seqId) new MySqlParameter(@"id", seqId)
); );
while (reader.Read()) { while(reader.Read()) {
IChatEvent evt = ReadEvent(reader); IChatEvent evt = ReadEvent(reader);
if (evt != null) if(evt != null)
return evt; return evt;
} }
} catch(MySqlException ex) { } catch(MySqlException ex) {

View file

@ -8,7 +8,7 @@ namespace SharpChat {
@"SELECT COUNT(*) FROM `sqc_migrations` WHERE `migration_name` = @name", @"SELECT COUNT(*) FROM `sqc_migrations` WHERE `migration_name` = @name",
new MySqlParameter(@"name", name) new MySqlParameter(@"name", name)
) > 0; ) > 0;
if (!done) { if(!done) {
Logger.Write($@"Running migration '{name}'..."); Logger.Write($@"Running migration '{name}'...");
action(); action();
RunCommand( RunCommand(

View file

@ -1,23 +1,22 @@
using System; using System.IO;
using System.IO;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
namespace SharpChat { namespace SharpChat {
public static class Extensions { public static class Extensions {
public static string GetSignedHash(this string str, string key = null) public static string GetSignedHash(this string str, string key = null) {
=> Encoding.UTF8.GetBytes(str).GetSignedHash(key); return Encoding.UTF8.GetBytes(str).GetSignedHash(key);
}
public static string GetSignedHash(this byte[] bytes, string key = null) { public static string GetSignedHash(this byte[] bytes, string key = null) {
if (key == null) key ??= File.Exists(@"login_key.txt") ? File.ReadAllText(@"login_key.txt") : @"woomy";
key = File.Exists(@"login_key.txt") ? File.ReadAllText(@"login_key.txt") : @"woomy";
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
using (HMACSHA256 algo = new HMACSHA256(Encoding.UTF8.GetBytes(key))) { using(HMACSHA256 algo = new(Encoding.UTF8.GetBytes(key))) {
byte[] hash = algo.ComputeHash(bytes); byte[] hash = algo.ComputeHash(bytes);
foreach (byte b in hash) foreach(byte b in hash)
sb.AppendFormat(@"{0:x2}", b); sb.AppendFormat(@"{0:x2}", b);
} }
@ -26,7 +25,7 @@ namespace SharpChat {
public static string GetIdString(this byte[] buffer) { public static string GetIdString(this byte[] buffer) {
const string id_chars = @"abcdefghijklmnopqrstuvwxyz0123456789-_ABCDEFGHIJKLMNOPQRSTUVWXYZ"; const string id_chars = @"abcdefghijklmnopqrstuvwxyz0123456789-_ABCDEFGHIJKLMNOPQRSTUVWXYZ";
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
foreach(byte b in buffer) foreach(byte b in buffer)
sb.Append(id_chars[b % id_chars.Length]); sb.Append(id_chars[b % id_chars.Length]);
return sb.ToString(); return sb.ToString();

View file

@ -19,8 +19,9 @@ namespace SharpChat.Flashii {
public string Hash public string Hash
=> string.Join(@"#", UserId, Token, IPAddress).GetSignedHash(); => string.Join(@"#", UserId, Token, IPAddress).GetSignedHash();
public byte[] GetJSON() public byte[] GetJSON() {
=> JsonSerializer.SerializeToUtf8Bytes(this); return JsonSerializer.SerializeToUtf8Bytes(this);
}
} }
public class FlashiiAuth { public class FlashiiAuth {
@ -55,7 +56,7 @@ namespace SharpChat.Flashii {
throw new ArgumentNullException(nameof(authRequest)); throw new ArgumentNullException(nameof(authRequest));
#if DEBUG #if DEBUG
if (authRequest.UserId >= 10000) if(authRequest.UserId >= 10000)
return new FlashiiAuth { return new FlashiiAuth {
Success = true, Success = true,
UserId = authRequest.UserId, UserId = authRequest.UserId,

View file

@ -12,7 +12,7 @@ namespace SharpChat.Flashii {
public static string AuthURL { get; } public static string AuthURL { get; }
public static string BansURL { get; } public static string BansURL { get; }
public static string BumpURL { get; } public static string BumpURL { get; }
static FlashiiUrls() { static FlashiiUrls() {
AuthURL = GetURL(AUTH); AuthURL = GetURL(AUTH);
BansURL = GetURL(BANS); BansURL = GetURL(BANS);

View file

@ -4,25 +4,31 @@ using System.Text;
namespace SharpChat { namespace SharpChat {
public static class Logger { public static class Logger {
public static void Write(string str) public static void Write(string str) {
=> Console.WriteLine(string.Format(@"[{1}] {0}", str, DateTime.Now)); Console.WriteLine(string.Format(@"[{1}] {0}", str, DateTime.Now));
}
public static void Write(byte[] bytes) public static void Write(byte[] bytes) {
=> Write(Encoding.UTF8.GetString(bytes)); Write(Encoding.UTF8.GetString(bytes));
}
public static void Write(object obj) public static void Write(object obj) {
=> Write(obj?.ToString() ?? string.Empty); Write(obj?.ToString() ?? string.Empty);
}
[Conditional(@"DEBUG")] [Conditional(@"DEBUG")]
public static void Debug(string str) public static void Debug(string str) {
=> Write(str); Write(str);
}
[Conditional(@"DEBUG")] [Conditional(@"DEBUG")]
public static void Debug(byte[] bytes) public static void Debug(byte[] bytes) {
=> Write(bytes); Write(bytes);
}
[Conditional(@"DEBUG")] [Conditional(@"DEBUG")]
public static void Debug(object obj) public static void Debug(object obj) {
=> Write(obj); Write(obj);
}
} }
} }

View file

@ -16,20 +16,20 @@ namespace SharpChat.Packet {
public AuthFailPacket(AuthFailReason reason, DateTimeOffset? expires = null) { public AuthFailPacket(AuthFailReason reason, DateTimeOffset? expires = null) {
Reason = reason; Reason = reason;
if (reason == AuthFailReason.Banned) { if(reason == AuthFailReason.Banned) {
if (!expires.HasValue) if(!expires.HasValue)
throw new ArgumentNullException(nameof(expires)); throw new ArgumentNullException(nameof(expires));
Expires = expires.Value; Expires = expires.Value;
} }
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('1'); sb.Append('1');
sb.Append("\tn\t"); sb.Append("\tn\t");
switch (Reason) { switch(Reason) {
case AuthFailReason.AuthInvalid: case AuthFailReason.AuthInvalid:
default: default:
sb.Append(@"authfail"); sb.Append(@"authfail");
@ -42,10 +42,10 @@ namespace SharpChat.Packet {
break; break;
} }
if (Reason == AuthFailReason.Banned) { if(Reason == AuthFailReason.Banned) {
sb.Append('\t'); sb.Append('\t');
if (Expires == DateTimeOffset.MaxValue) if(Expires == DateTimeOffset.MaxValue)
sb.Append(@"-1"); sb.Append(@"-1");
else else
sb.Append(Expires.ToUnixTimeSeconds()); sb.Append(Expires.ToUnixTimeSeconds());

View file

@ -15,7 +15,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('1'); sb.Append('1');
sb.Append("\ty\t"); sb.Append("\ty\t");

View file

@ -12,17 +12,17 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('2'); sb.Append('2');
sb.Append('\t'); sb.Append('\t');
sb.Append(DateTimeOffset.Now.ToUnixTimeSeconds()); sb.Append(DateTimeOffset.Now.ToUnixTimeSeconds());
sb.Append("\t-1\t0\fbanlist\f"); sb.Append("\t-1\t0\fbanlist\f");
foreach (IBan ban in Bans) foreach(IBan ban in Bans)
sb.AppendFormat(@"<a href=""javascript:void(0);"" onclick=""Chat.SendMessageWrapper('/unban '+ this.innerHTML);"">{0}</a>, ", ban); sb.AppendFormat(@"<a href=""javascript:void(0);"" onclick=""Chat.SendMessageWrapper('/unban '+ this.innerHTML);"">{0}</a>, ", ban);
if (Bans.Any()) if(Bans.Any())
sb.Length -= 2; sb.Length -= 2;
sb.Append('\t'); sb.Append('\t');

View file

@ -10,7 +10,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('4'); sb.Append('4');
sb.Append('\t'); sb.Append('\t');

View file

@ -11,7 +11,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('4'); sb.Append('4');
sb.Append('\t'); sb.Append('\t');

View file

@ -12,7 +12,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('4'); sb.Append('4');
sb.Append('\t'); sb.Append('\t');

View file

@ -10,12 +10,12 @@ namespace SharpChat.Packet {
public ChatMessageAddPacket(IChatMessage message) : base(message?.SequenceId ?? 0) { public ChatMessageAddPacket(IChatMessage message) : base(message?.SequenceId ?? 0) {
Message = message ?? throw new ArgumentNullException(nameof(message)); Message = message ?? throw new ArgumentNullException(nameof(message));
if (Message.SequenceId < 1) if(Message.SequenceId < 1)
Message.SequenceId = SequenceId; Message.SequenceId = SequenceId;
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('2'); sb.Append('2');
sb.Append('\t'); sb.Append('\t');
@ -26,7 +26,7 @@ namespace SharpChat.Packet {
sb.Append(Message.Sender?.UserId ?? -1); sb.Append(Message.Sender?.UserId ?? -1);
sb.Append('\t'); sb.Append('\t');
if (Message.Flags.HasFlag(ChatMessageFlags.Action)) if(Message.Flags.HasFlag(ChatMessageFlags.Action))
sb.Append(@"<i>"); sb.Append(@"<i>");
sb.Append( sb.Append(
@ -37,7 +37,7 @@ namespace SharpChat.Packet {
.Replace("\t", @" ") .Replace("\t", @" ")
); );
if (Message.Flags.HasFlag(ChatMessageFlags.Action)) if(Message.Flags.HasFlag(ChatMessageFlags.Action))
sb.Append(@"</i>"); sb.Append(@"</i>");
sb.Append('\t'); sb.Append('\t');

View file

@ -10,7 +10,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('6'); sb.Append('6');
sb.Append('\t'); sb.Append('\t');

View file

@ -12,7 +12,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('7'); sb.Append('7');
sb.Append('\t'); sb.Append('\t');
@ -20,7 +20,7 @@ namespace SharpChat.Packet {
sb.Append('\t'); sb.Append('\t');
sb.Append(Channels.Count()); sb.Append(Channels.Count());
foreach (ChatChannel channel in Channels) { foreach(ChatChannel channel in Channels) {
sb.Append('\t'); sb.Append('\t');
sb.Append(channel.Pack()); sb.Append(channel.Pack());
} }

View file

@ -23,7 +23,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('8'); sb.Append('8');
sb.Append('\t'); sb.Append('\t');

View file

@ -16,7 +16,7 @@ namespace SharpChat.Packet {
private const string V1_CHATBOT = "-1\tChatBot\tinherit\t\t"; private const string V1_CHATBOT = "-1\tChatBot\tinherit\t\t";
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('7'); sb.Append('7');
sb.Append('\t'); sb.Append('\t');
@ -25,7 +25,7 @@ namespace SharpChat.Packet {
sb.Append(Event.DateTime.ToUnixTimeSeconds()); sb.Append(Event.DateTime.ToUnixTimeSeconds());
sb.Append('\t'); sb.Append('\t');
switch (Event) { switch(Event) {
case IChatMessage msg: case IChatMessage msg:
sb.Append(Event.Sender.Pack()); sb.Append(Event.Sender.Pack());
sb.Append('\t'); sb.Append('\t');
@ -60,7 +60,7 @@ namespace SharpChat.Packet {
sb.Append(V1_CHATBOT); sb.Append(V1_CHATBOT);
sb.Append("0\f"); sb.Append("0\f");
switch (ude.Reason) { switch(ude.Reason) {
case UserDisconnectReason.Flood: case UserDisconnectReason.Flood:
sb.Append(@"flood"); sb.Append(@"flood");
break; break;

View file

@ -12,7 +12,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('7'); sb.Append('7');
sb.Append('\t'); sb.Append('\t');
@ -20,7 +20,7 @@ namespace SharpChat.Packet {
sb.Append('\t'); sb.Append('\t');
sb.Append(Users.Count()); sb.Append(Users.Count());
foreach (ChatUser user in Users) { foreach(ChatUser user in Users) {
sb.Append('\t'); sb.Append('\t');
sb.Append(user.Pack()); sb.Append(user.Pack());
sb.Append('\t'); sb.Append('\t');

View file

@ -5,7 +5,7 @@ using System.Text;
namespace SharpChat.Packet { namespace SharpChat.Packet {
public class FloodWarningPacket : ServerPacket { public class FloodWarningPacket : ServerPacket {
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('2'); sb.Append('2');
sb.Append('\t'); sb.Append('\t');

View file

@ -15,21 +15,21 @@ namespace SharpChat.Packet {
public ForceDisconnectPacket(ForceDisconnectReason reason, DateTimeOffset? expires = null) { public ForceDisconnectPacket(ForceDisconnectReason reason, DateTimeOffset? expires = null) {
Reason = reason; Reason = reason;
if (reason == ForceDisconnectReason.Banned) { if(reason == ForceDisconnectReason.Banned) {
if (!expires.HasValue) if(!expires.HasValue)
throw new ArgumentNullException(nameof(expires)); throw new ArgumentNullException(nameof(expires));
Expires = expires.Value; Expires = expires.Value;
} }
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('9'); sb.Append('9');
sb.Append('\t'); sb.Append('\t');
sb.Append((int)Reason); sb.Append((int)Reason);
if (Reason == ForceDisconnectReason.Banned) { if(Reason == ForceDisconnectReason.Banned) {
sb.Append('\t'); sb.Append('\t');
sb.Append(Expires.ToUnixTimeSeconds()); sb.Append(Expires.ToUnixTimeSeconds());
} }

View file

@ -20,9 +20,9 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
if (StringId == LCR.WELCOME) { if(StringId == LCR.WELCOME) {
sb.Append('7'); sb.Append('7');
sb.Append('\t'); sb.Append('\t');
sb.Append('1'); sb.Append('1');
@ -40,15 +40,15 @@ namespace SharpChat.Packet {
sb.Append('\f'); sb.Append('\f');
sb.Append(StringId == LCR.WELCOME ? LCR.BROADCAST : StringId); sb.Append(StringId == LCR.WELCOME ? LCR.BROADCAST : StringId);
if (Arguments?.Any() == true) if(Arguments?.Any() == true)
foreach (object arg in Arguments) { foreach(object arg in Arguments) {
sb.Append('\f'); sb.Append('\f');
sb.Append(arg); sb.Append(arg);
} }
sb.Append('\t'); sb.Append('\t');
if (StringId == LCR.WELCOME) { if(StringId == LCR.WELCOME) {
sb.Append(StringId); sb.Append(StringId);
sb.Append("\t0"); sb.Append("\t0");
} else } else

View file

@ -11,7 +11,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('0'); sb.Append('0');
sb.Append('\t'); sb.Append('\t');

View file

@ -11,7 +11,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('5'); sb.Append('5');
sb.Append('\t'); sb.Append('\t');

View file

@ -11,7 +11,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('5'); sb.Append('5');
sb.Append('\t'); sb.Append('\t');

View file

@ -11,7 +11,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('5'); sb.Append('5');
sb.Append('\t'); sb.Append('\t');

View file

@ -13,7 +13,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('1'); sb.Append('1');
sb.Append('\t'); sb.Append('\t');

View file

@ -22,7 +22,7 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
sb.Append('3'); sb.Append('3');
sb.Append('\t'); sb.Append('\t');
@ -31,7 +31,7 @@ namespace SharpChat.Packet {
sb.Append(User.DisplayName); sb.Append(User.DisplayName);
sb.Append('\t'); sb.Append('\t');
switch (Reason) { switch(Reason) {
case UserDisconnectReason.Leave: case UserDisconnectReason.Leave:
default: default:
sb.Append(@"leave"); sb.Append(@"leave");

View file

@ -13,11 +13,11 @@ namespace SharpChat.Packet {
} }
public override IEnumerable<string> Pack() { public override IEnumerable<string> Pack() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new();
bool isSilent = string.IsNullOrEmpty(PreviousName); bool isSilent = string.IsNullOrEmpty(PreviousName);
if (!isSilent) { if(!isSilent) {
sb.Append('2'); sb.Append('2');
sb.Append('\t'); sb.Append('\t');
sb.Append(DateTimeOffset.Now.ToUnixTimeSeconds()); sb.Append(DateTimeOffset.Now.ToUnixTimeSeconds());

View file

@ -3,22 +3,22 @@ using System.Security.Cryptography;
namespace SharpChat { namespace SharpChat {
public static class RNG { public static class RNG {
private static object Lock { get; } = new object(); private static object Lock { get; } = new();
private static Random NormalRandom { get; } = new Random(); private static Random NormalRandom { get; } = new();
private static RandomNumberGenerator SecureRandom { get; } = RandomNumberGenerator.Create(); private static RandomNumberGenerator SecureRandom { get; } = RandomNumberGenerator.Create();
public static int Next() { public static int Next() {
lock (Lock) lock(Lock)
return NormalRandom.Next(); return NormalRandom.Next();
} }
public static int Next(int max) { public static int Next(int max) {
lock (Lock) lock(Lock)
return NormalRandom.Next(max); return NormalRandom.Next(max);
} }
public static int Next(int min, int max) { public static int Next(int min, int max) {
lock (Lock) lock(Lock)
return NormalRandom.Next(min, max); return NormalRandom.Next(min, max);
} }

View file

@ -21,7 +21,7 @@ namespace SharpChat {
private Action<IWebSocketConnection> _config; private Action<IWebSocketConnection> _config;
public SharpChatWebSocketServer(string location, bool supportDualStack = true) { public SharpChatWebSocketServer(string location, bool supportDualStack = true) {
Uri uri = new Uri(location); Uri uri = new(location);
Port = uri.Port; Port = uri.Port;
Location = location; Location = location;
@ -29,15 +29,15 @@ namespace SharpChat {
_locationIP = ParseIPAddress(uri); _locationIP = ParseIPAddress(uri);
_scheme = uri.Scheme; _scheme = uri.Scheme;
Socket socket = new Socket(_locationIP.AddressFamily, SocketType.Stream, ProtocolType.IP); Socket socket = new(_locationIP.AddressFamily, SocketType.Stream, ProtocolType.IP);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
if (SupportDualStack && Type.GetType(@"Mono.Runtime") == null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { if(SupportDualStack && Type.GetType(@"Mono.Runtime") == null && RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false); socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false);
} }
ListenerSocket = new SocketWrapper(socket); ListenerSocket = new SocketWrapper(socket);
SupportedSubProtocols = new string[0]; SupportedSubProtocols = Array.Empty<string>();
} }
public ISocket ListenerSocket { get; set; } public ISocket ListenerSocket { get; set; }
@ -55,37 +55,38 @@ namespace SharpChat {
public void Dispose() { public void Dispose() {
ListenerSocket.Dispose(); ListenerSocket.Dispose();
GC.SuppressFinalize(this);
} }
private IPAddress ParseIPAddress(Uri uri) { private static IPAddress ParseIPAddress(Uri uri) {
string ipStr = uri.Host; string ipStr = uri.Host;
if (ipStr == "0.0.0.0") { if(ipStr == "0.0.0.0") {
return IPAddress.Any; return IPAddress.Any;
} else if (ipStr == "[0000:0000:0000:0000:0000:0000:0000:0000]") { } else if(ipStr == "[0000:0000:0000:0000:0000:0000:0000:0000]") {
return IPAddress.IPv6Any; return IPAddress.IPv6Any;
} else { } else {
try { try {
return IPAddress.Parse(ipStr); return IPAddress.Parse(ipStr);
} catch (Exception ex) { } catch(Exception ex) {
throw new FormatException("Failed to parse the IP address part of the location. Please make sure you specify a valid IP address. Use 0.0.0.0 or [::] to listen on all interfaces.", ex); throw new FormatException("Failed to parse the IP address part of the location. Please make sure you specify a valid IP address. Use 0.0.0.0 or [::] to listen on all interfaces.", ex);
} }
} }
} }
public void Start(Action<IWebSocketConnection> config) { public void Start(Action<IWebSocketConnection> config) {
IPEndPoint ipLocal = new IPEndPoint(_locationIP, Port); IPEndPoint ipLocal = new(_locationIP, Port);
ListenerSocket.Bind(ipLocal); ListenerSocket.Bind(ipLocal);
ListenerSocket.Listen(100); ListenerSocket.Listen(100);
Port = ((IPEndPoint)ListenerSocket.LocalEndPoint).Port; Port = ((IPEndPoint)ListenerSocket.LocalEndPoint).Port;
FleckLog.Info(string.Format("Server started at {0} (actual port {1})", Location, Port)); FleckLog.Info(string.Format("Server started at {0} (actual port {1})", Location, Port));
if (_scheme == "wss") { if(_scheme == "wss") {
if (Certificate == null) { if(Certificate == null) {
FleckLog.Error("Scheme cannot be 'wss' without a Certificate"); FleckLog.Error("Scheme cannot be 'wss' without a Certificate");
return; return;
} }
if (EnabledSslProtocols == SslProtocols.None) { if(EnabledSslProtocols == SslProtocols.None) {
EnabledSslProtocols = SslProtocols.Tls; EnabledSslProtocols = SslProtocols.Tls;
FleckLog.Debug("Using default TLS 1.0 security protocol."); FleckLog.Debug("Using default TLS 1.0 security protocol.");
} }
@ -97,16 +98,16 @@ namespace SharpChat {
private void ListenForClients() { private void ListenForClients() {
ListenerSocket.Accept(OnClientConnect, e => { ListenerSocket.Accept(OnClientConnect, e => {
FleckLog.Error("Listener socket is closed", e); FleckLog.Error("Listener socket is closed", e);
if (RestartAfterListenError) { if(RestartAfterListenError) {
FleckLog.Info("Listener socket restarting"); FleckLog.Info("Listener socket restarting");
try { try {
ListenerSocket.Dispose(); ListenerSocket.Dispose();
Socket socket = new Socket(_locationIP.AddressFamily, SocketType.Stream, ProtocolType.IP); Socket socket = new(_locationIP.AddressFamily, SocketType.Stream, ProtocolType.IP);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1); socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
ListenerSocket = new SocketWrapper(socket); ListenerSocket = new SocketWrapper(socket);
Start(_config); Start(_config);
FleckLog.Info("Listener socket restarted"); FleckLog.Info("Listener socket restarted");
} catch (Exception ex) { } catch(Exception ex) {
FleckLog.Error("Listener could not be restarted", ex); FleckLog.Error("Listener could not be restarted", ex);
} }
} }
@ -114,7 +115,7 @@ namespace SharpChat {
} }
private void OnClientConnect(ISocket clientSocket) { private void OnClientConnect(ISocket clientSocket) {
if (clientSocket == null) return; // socket closed if(clientSocket == null) return; // socket closed
FleckLog.Debug(string.Format("Client connected from {0}:{1}", clientSocket.RemoteIpAddress, clientSocket.RemotePort.ToString())); FleckLog.Debug(string.Format("Client connected from {0}:{1}", clientSocket.RemoteIpAddress, clientSocket.RemotePort.ToString()));
ListenForClients(); ListenForClients();
@ -151,7 +152,7 @@ namespace SharpChat {
}, },
s => SubProtocolNegotiator.Negotiate(SupportedSubProtocols, s)); s => SubProtocolNegotiator.Negotiate(SupportedSubProtocols, s));
if (IsSecure) { if(IsSecure) {
FleckLog.Debug("Authenticating Secure Connection"); FleckLog.Debug("Authenticating Secure Connection");
clientSocket clientSocket
.Authenticate(Certificate, .Authenticate(Certificate,

View file

@ -4,7 +4,7 @@ using System.Linq;
namespace SharpChat { namespace SharpChat {
public class UserManager : IDisposable { public class UserManager : IDisposable {
private readonly List<ChatUser> Users = new List<ChatUser>(); private readonly List<ChatUser> Users = new();
public readonly ChatContext Context; public readonly ChatContext Context;
@ -15,7 +15,7 @@ namespace SharpChat {
} }
public void Add(ChatUser user) { public void Add(ChatUser user) {
if (user == null) if(user == null)
throw new ArgumentNullException(nameof(user)); throw new ArgumentNullException(nameof(user));
lock(Users) lock(Users)
@ -24,7 +24,7 @@ namespace SharpChat {
} }
public void Remove(ChatUser user) { public void Remove(ChatUser user) {
if (user == null) if(user == null)
return; return;
lock(Users) lock(Users)
@ -32,10 +32,10 @@ namespace SharpChat {
} }
public bool Contains(ChatUser user) { public bool Contains(ChatUser user) {
if (user == null) if(user == null)
return false; return false;
lock (Users) lock(Users)
return Users.Contains(user) || Users.Any(x => x.UserId == user.UserId || x.Username.ToLowerInvariant() == user.Username.ToLowerInvariant()); return Users.Contains(user) || Users.Any(x => x.UserId == user.UserId || x.Username.ToLowerInvariant() == user.Username.ToLowerInvariant());
} }
@ -45,7 +45,7 @@ namespace SharpChat {
} }
public ChatUser Get(string username, bool includeNickName = true, bool includeDisplayName = true) { public ChatUser Get(string username, bool includeNickName = true, bool includeDisplayName = true) {
if (string.IsNullOrWhiteSpace(username)) if(string.IsNullOrWhiteSpace(username))
return null; return null;
username = username.ToLowerInvariant(); username = username.ToLowerInvariant();
@ -56,35 +56,35 @@ namespace SharpChat {
} }
public IEnumerable<ChatUser> OfHierarchy(int hierarchy) { public IEnumerable<ChatUser> OfHierarchy(int hierarchy) {
lock (Users) lock(Users)
return Users.Where(u => u.Rank >= hierarchy).ToList(); return Users.Where(u => u.Rank >= hierarchy).ToList();
} }
public IEnumerable<ChatUser> WithActiveConnections() { public IEnumerable<ChatUser> WithActiveConnections() {
lock (Users) lock(Users)
return Users.Where(u => u.HasSessions).ToList(); return Users.Where(u => u.HasSessions).ToList();
} }
public IEnumerable<ChatUser> All() { public IEnumerable<ChatUser> All() {
lock (Users) lock(Users)
return Users.ToList(); return Users.ToList();
} }
~UserManager()
=> Dispose(false);
public void Dispose() ~UserManager() {
=> Dispose(true); DoDispose();
}
private void Dispose(bool disposing) { public void Dispose() {
if (IsDisposed) DoDispose();
GC.SuppressFinalize(this);
}
private void DoDispose() {
if(IsDisposed)
return; return;
IsDisposed = true; IsDisposed = true;
Users.Clear(); Users.Clear();
if (disposing)
GC.SuppressFinalize(this);
} }
} }
} }