From aef26a40cbc6835aae0d293a523f5a7681b9a436 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Thu, 14 May 2020 17:13:46 -0400 Subject: wow lots of tests passing! --- IrcStates/Server.cs | 390 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 316 insertions(+), 74 deletions(-) (limited to 'IrcStates/Server.cs') diff --git a/IrcStates/Server.cs b/IrcStates/Server.cs index d9022a0..ca812f6 100644 --- a/IrcStates/Server.cs +++ b/IrcStates/Server.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Design; using System.Globalization; using System.Linq; using IrcTokens; @@ -59,25 +60,26 @@ namespace IrcStates return Casemap.CaseFold(ISupport.CaseMapping, str); } - public bool CaseFoldEquals(string s1, string s2) + private bool CaseFoldEquals(string s1, string s2) { return CaseFold(s1) == CaseFold(s2); } - public bool IsMe(string nickname) + private bool IsMe(string nickname) { return CaseFold(nickname) == NickNameLower; } - public bool HasUser(string nickname) + private bool HasUser(string nickname) { return Users.ContainsKey(CaseFold(nickname)); } - private void AddUser(string nickname, string nicknameLower) + private User AddUser(string nickname, string nicknameLower) { var user = CreateUser(nickname, nicknameLower); Users[nicknameLower] = user; + return user; } private User CreateUser(string nickname, string nicknameLower) @@ -87,18 +89,18 @@ namespace IrcStates return user; } - public bool IsChannel(string target) + private bool IsChannel(string target) { return !string.IsNullOrEmpty(target) && ISupport.ChanTypes.Contains(target[0].ToString(CultureInfo.InvariantCulture)); } - public bool HasChannel(string name) + private bool HasChannel(string name) { return Channels.ContainsKey(CaseFold(name)); } - public Channel GetChannel(string name) + private Channel GetChannel(string name) { return HasChannel(name) ? Channels[name] : null; } @@ -138,7 +140,7 @@ namespace IrcStates if (!user.Channels.Any()) Users.Remove(nickLower); } - if (nickLower == NickNameLower) + if (IsMe(nickName)) { Channels.Remove(channelLower); foreach (var userToRemove in channel.Users.Keys.Select(u => Users[u])) @@ -152,6 +154,53 @@ namespace IrcStates return (emit, user); } + private void SetChannelModes(Channel channel, IEnumerable<(bool, string)> modes, IList parameters) + { + foreach (var (add, c) in modes) + { + var listMode = ISupport.ChanModes.ListModes.Contains(c); + if (ISupport.Prefix.Modes.Contains(c)) + { + var nicknameLower = CaseFold(parameters.First()); + parameters.RemoveAt(0); + if (!HasUser(nicknameLower)) continue; + + var channelUser = channel.Users[nicknameLower]; + if (add) + { + if (!channelUser.Modes.Contains(c)) + { + channelUser.Modes.Add(c); + } + } + else if (channelUser.Modes.Contains(c)) + { + channelUser.Modes.Remove(c); + } + } + else if (add && (listMode || + ISupport.ChanModes.SettingBModes.Contains(c) || + ISupport.ChanModes.SettingCModes.Contains(c))) + { + channel.AddMode(c, parameters.First(), listMode); + parameters.RemoveAt(0); + } + else if (!add && (listMode || ISupport.ChanModes.SettingBModes.Contains(c))) + { + channel.RemoveMode(c, parameters.First()); + parameters.RemoveAt(0); + } + else if (add) + { + channel.AddMode(c, null, false); + } + else + { + channel.RemoveMode(c, null); + } + } + } + public List<(Line, Emit)> Recv(byte[] data) { if (data == null) return null; @@ -166,45 +215,49 @@ namespace IrcStates { if (line == null) return null; - switch (line.Command) - { - case Numeric.RPL_WELCOME: return HandleWelcome(line); - case Numeric.RPL_ISUPPORT: return HandleISupport(line); - case Numeric.RPL_MOTDSTART: - case Numeric.RPL_MOTD: - return HandleMotd(line); - case Commands.Nick: return HandleNick(line); - case Commands.Join: return HandleJoin(line); - case Commands.Part: return HandlePart(line); - case Commands.Kick: return HandleKick(line); - case Commands.Quit: return HandleQuit(line); - case Commands.Error: return HandleError(line); - case Numeric.RPL_NAMREPLY: return HandleNames(line); - case Numeric.RPL_CREATIONTIME: return HandleCreationTime(line); - case Commands.Topic: return HandleTopic(line); - case Numeric.RPL_TOPIC: return HandleTopicNumeric(line); - case Numeric.RPL_TOPICWHOTIME: return HandleTopicTime(line); - case Commands.Mode: return HandleMode(line); - case Numeric.RPL_CHANNELMODEIS: return HandleChannelModeIs(line); - case Numeric.RPL_UMODEIS: return HandleUModeIs(line); - case Commands.Privmsg: - case Commands.Notice: - case Commands.Tagmsg: - return HandleMessage(line); - case Numeric.RPL_VISIBLEHOST: return HandleVisibleHost(line); - case Numeric.RPL_WHOREPLY: return HandleWhoReply(line); - case Numeric.RPL_WHOSPCRPL: return HandleWhox(line); - case Numeric.RPL_WHOISUSER: return HandleWhoIsUser(line); - case Commands.Chghost: return HandleChghost(line); - case Commands.Setname: return HandleSetname(line); - case Commands.Away: return HandleAway(line); - case Commands.Account: return HandleAccount(line); - case Commands.Cap: return HandleCap(line); - case Numeric.RPL_LOGGEDIN: return HandleLoggedIn(line); - case Numeric.RPL_LOGGEDOUT: return HandleLoggedOut(line); - } + var emit = line.Command switch + { + Numeric.RPL_WELCOME => HandleWelcome(line), + Numeric.RPL_ISUPPORT => HandleISupport(line), + Numeric.RPL_MOTDSTART => HandleMotd(line), + Numeric.RPL_MOTD => HandleMotd(line), + Commands.Nick => HandleNick(line), + Commands.Join => HandleJoin(line), + Commands.Part => HandlePart(line), + Commands.Kick => HandleKick(line), + Commands.Quit => HandleQuit(line), + Commands.Error => HandleError(line), + Numeric.RPL_NAMREPLY => HandleNames(line), + Numeric.RPL_CREATIONTIME => HandleCreationTime(line), + Commands.Topic => HandleTopic(line), + Numeric.RPL_TOPIC => HandleTopicNumeric(line), + Numeric.RPL_TOPICWHOTIME => HandleTopicTime(line), + Commands.Mode => HandleMode(line), + Numeric.RPL_CHANNELMODEIS => HandleChannelModeIs(line), + Numeric.RPL_UMODEIS => HandleUModeIs(line), + Commands.Privmsg => HandleMessage(line), + Commands.Notice => HandleMessage(line), + Commands.Tagmsg => HandleMessage(line), + Numeric.RPL_VISIBLEHOST => HandleVisibleHost(line), + Numeric.RPL_WHOREPLY => HandleWhoReply(line), + Numeric.RPL_WHOSPCRPL => HandleWhox(line), + Numeric.RPL_WHOISUSER => HandleWhoIsUser(line), + Commands.Chghost => HandleChghost(line), + Commands.Setname => HandleSetname(line), + Commands.Away => HandleAway(line), + Commands.Account => HandleAccount(line), + Commands.Cap => HandleCap(line), + Numeric.RPL_LOGGEDIN => HandleLoggedIn(line), + Numeric.RPL_LOGGEDOUT => HandleLoggedOut(line), + _ => null + }; + + if (emit != null) + emit.Command = line.Command; + else + emit = new Emit(); - return new Emit(); + return emit; } private Emit HandleSetname(Line line) @@ -213,7 +266,7 @@ namespace IrcStates var realname = line.Params[0]; var nicknameLower = CaseFold(line.Hostmask.NickName); - if (nicknameLower == NickNameLower) + if (IsMe(nicknameLower)) { emit.Self = true; RealName = realname; @@ -235,7 +288,7 @@ namespace IrcStates var away = line.Params.FirstOrDefault(); var nicknameLower = CaseFold(line.Hostmask.NickName); - if (nicknameLower == NickNameLower) + if (IsMe(nicknameLower)) { emit.Self = true; Away = away; @@ -257,7 +310,7 @@ namespace IrcStates var account = line.Params[0].Trim('*'); var nicknameLower = CaseFold(line.Hostmask.NickName); - if (nicknameLower == NickNameLower) + if (IsMe(nicknameLower)) { emit.Self = true; Account = account; @@ -289,24 +342,21 @@ namespace IrcStates tokens[kv[0]] = kv.Length > 1 ? kv[1] : string.Empty; } - var emit = new Emit(); - emit.Subcommand = subcommand; - emit.Finished = !multiline; - emit.Tokens = tokensStr; + var emit = new Emit {Subcommand = subcommand, Finished = !multiline, Tokens = tokensStr}; switch (subcommand) { case "LS": - TempCaps = tokens; + TempCaps.UpdateWith(tokens); if (!multiline) { - AvailableCaps = TempCaps; + AvailableCaps.UpdateWith(TempCaps); TempCaps.Clear(); } break; case "NEW": - AvailableCaps.Update(tokens); + AvailableCaps.UpdateWith(tokens); break; case "DEL": foreach (var key in tokens.Keys.Where(key => AvailableCaps.ContainsKey(key))) @@ -323,7 +373,7 @@ namespace IrcStates var k = key.Substring(1); if (AgreedCaps.Contains(k)) AgreedCaps.Remove(k); } - else if (!AgreedCaps.Contains(key) && !AvailableCaps.ContainsKey(key)) + else if (!AgreedCaps.Contains(key) && AvailableCaps.ContainsKey(key)) { AgreedCaps.Add(key); } @@ -348,7 +398,7 @@ namespace IrcStates var hostname = line.Params[1]; var nicknameLower = CaseFold(line.Hostmask.NickName); - if (nicknameLower == NickNameLower) + if (IsMe(nicknameLower)) { emit.Self = true; UserName = username; @@ -368,42 +418,234 @@ namespace IrcStates private Emit HandleWhoIsUser(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + var nickname = line.Params[1]; + var username = line.Params[2]; + var hostname = line.Params[3]; + var realname = line.Params[5]; + + if (IsMe(nickname)) + { + emit.Self = true; + UserName = username; + HostName = hostname; + RealName = realname; + } + + if (HasUser(nickname)) + { + var user = Users[CaseFold(nickname)]; + emit.User = user; + user.UserName = username; + user.HostName = hostname; + user.RealName = realname; + } + + return emit; } private Emit HandleWhox(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + if (line.Params[1] == WhoType && line.Params.Count == 8) + { + var nickname = line.Params[5]; + var username = line.Params[2]; + var hostname = line.Params[4]; + var realname = line.Params[7]; + var account = line.Params[6] == "0" ? null : line.Params[6]; + + if (IsMe(nickname)) + { + emit.Self = true; + UserName = username; + HostName = hostname; + RealName = realname; + Account = account; + } + + if (HasUser(nickname)) + { + var user = Users[CaseFold(nickname)]; + emit.User = user; + user.UserName = username; + user.HostName = hostname; + user.RealName = realname; + user.Account = account; + } + } + + return emit; } private Emit HandleWhoReply(Line line) { - throw new NotImplementedException(); + var emit = new Emit {Target = line.Params[1]}; + var nickname = line.Params[5]; + var username = line.Params[2]; + var hostname = line.Params[3]; + var realname = line.Params[7].Split(' ', 2)[1]; + + if (IsMe(nickname)) + { + emit.Self = true; + UserName = username; + HostName = hostname; + RealName = realname; + } + + if (HasUser(nickname)) + { + var user = Users[CaseFold(nickname)]; + emit.User = user; + user.UserName = username; + user.HostName = hostname; + user.RealName = realname; + } + + return emit; } private Emit HandleVisibleHost(Line line) { - throw new NotImplementedException(); + var split = line.Params[1].Split('@', 2); + switch (split.Length) + { + case 1: + HostName = split[0]; + break; + case 2: + HostName = split[1]; + UserName = split[0]; + break; + } + + return new Emit(); } private Emit HandleMessage(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + var message = line.Params.Count > 1 ? line.Params[1] : null; + if (message != null) emit.Text = message; + + var nickLower = CaseFold(line.Hostmask.NickName); + if (IsMe(nickLower)) + { + emit.SelfSource = true; + SelfHostmask(line.Hostmask); + } + + var user = HasUser(nickLower) + ? Users[nickLower] + : AddUser(line.Hostmask.NickName, nickLower); + emit.User = user; + + if (line.Hostmask.UserName != null) user.UserName = line.Hostmask.UserName; + if (line.Hostmask.HostName != null) user.HostName = line.Hostmask.HostName; + + var target = line.Params[0]; + var statusMsg = new List(); + while (target.Length > 0) + { + var t = target[0].ToString(CultureInfo.InvariantCulture); + if (ISupport.StatusMsg.Contains(t)) + { + statusMsg.Add(t); + target = target.Substring(1); + } + else + break; + } + + emit.Target = line.Params[0]; + + if (IsChannel(target) && HasChannel(target)) + { + emit.Channel = Channels[CaseFold(target)]; + } + else if (IsMe(target)) + { + emit.SelfTarget = true; + } + + return emit; } private Emit HandleUModeIs(Line line) { - throw new NotImplementedException(); + foreach (var c in line.Params[1] + .TrimStart('+') + .Select(m => m.ToString(CultureInfo.InvariantCulture)) + .Where(m => !Modes.Contains(m))) + { + Modes.Add(c); + } + + return new Emit(); } private Emit HandleChannelModeIs(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + if (HasChannel(line.Params[1])) + { + var channel = Channels[CaseFold(line.Params[1])]; + emit.Channel = channel; + var modes = line.Params[2] + .TrimStart('+') + .Select(p => (true, p.ToString(CultureInfo.InvariantCulture))); + var parameters = line.Params.Skip(3).ToList(); + SetChannelModes(channel, modes, parameters); + } + + return emit; } private Emit HandleMode(Line line) { - throw new NotImplementedException(); + var emit = new Emit(); + var target = line.Params[0]; + var modeString = line.Params[1]; + var parameters = line.Params.Skip(2).ToList(); + + var modifier = '+'; + var modes = new List<(bool, string)>(); + var tokens = new List(); + + foreach (var c in modeString) + { + if (new[] {'+', '-'}.Contains(c)) + { + modifier = c; + } + else + { + modes.Add((modifier == '+', c.ToString(CultureInfo.InvariantCulture))); + tokens.Add($"{modifier}{c}"); + } + } + + emit.Tokens = tokens; + + if (IsMe(target)) + { + emit.SelfTarget = true; + foreach (var (add, c) in modes) + { + if (add && !Modes.Contains(c)) + Modes.Add(c); + else if (Modes.Contains(c)) Modes.Remove(c); + } + } + else if (HasChannel(target)) + { + var channel = GetChannel(CaseFold(target)); + emit.Channel = channel; + SetChannelModes(channel, modes, parameters); + } + + return emit; } private Emit HandleTopicTime(Line line) @@ -502,7 +744,7 @@ namespace IrcStates if (hostmask.UserName != null) user.UserName = hostmask.UserName; if (hostmask.HostName != null) user.HostName = hostmask.HostName; - if (nickLower == NickNameLower) SelfHostmask(hostmask); + if (IsMe(nickLower)) SelfHostmask(hostmask); foreach (var mode in modes.Select(c => c.ToString(CultureInfo.InvariantCulture))) if (!channelUser.Modes.Contains(mode)) @@ -525,7 +767,7 @@ namespace IrcStates var nickLower = CaseFold(line.Hostmask.NickName); if (line.Params.Any()) emit.Text = line.Params[0]; - if (nickLower == NickNameLower || line.Source == null) + if (IsMe(nickLower) || line.Source == null) { emit.Self = true; Users.Clear(); @@ -556,10 +798,10 @@ namespace IrcStates if (kicked != null) { emit.UserTarget = kicked; - if (kicked.NickNameLower == NickNameLower) emit.Self = true; + if (IsMe(kicked.NickName)) emit.Self = true; - var kickerLower = CaseFold(line.Hostmask.NickName); - if (kickerLower == NickNameLower) emit.SelfSource = true; + var kickerLower = CaseFold(line.Hostmask.NickName); + if (IsMe(kickerLower)) emit.SelfSource = true; emit.UserSource = Users.ContainsKey(kickerLower) ? Users[kickerLower] @@ -575,7 +817,7 @@ namespace IrcStates if (user != null) { emit.User = user; - if (user.NickNameLower == NickNameLower) emit.Self = true; + if (IsMe(user.NickName)) emit.Self = true; } return emit; @@ -593,7 +835,7 @@ namespace IrcStates var nickLower = CaseFold(line.Hostmask.NickName); // handle own join - if (nickLower == NickNameLower) + if (IsMe(nickLower)) { emit.Self = true; if (!HasChannel(channelLower)) @@ -661,7 +903,7 @@ namespace IrcStates } } - if (nickLower == NickNameLower) + if (IsMe(nickLower)) { emit.Self = true; NickName = nick; -- cgit 1.4.1