예제 #1
0
        public bool LoadPlugin(IApplicationBuilder app, string filename, bool isDep = false)
        {
            try
            {
                using var strm = File.OpenRead(filename); // Load from stream instead, allow us to live reload.
                var asm = _context.LoadFromStream(strm);
                if (asm == null)
                {
                    return(false);
                }

                _loadedPlugins.Add(filename, asm);
                _ev.LoadAssembly(asm);
                _logger.LogInformation("Loaded new Assembly {asm}", asm);

                // Register ASP.NET Core Controllers
                _appPartManager.ApplicationParts.Add(new AssemblyPart(asm));

                if (!isDep)
                {
                    GetEntryPoint(asm)?.OnEnable(app);
                }

                return(true);
            }
            catch (Exception ex)
            {
                Logger.Err(ex);
                return(false);
            }
        }
예제 #2
0
 public bool UnloadPlugins()
 {
     try
     {
         _context.Unload();
         return(true);
     }
     catch (Exception ex)
     {
         Logger.Err(ex);
         return(false);
     }
 }
예제 #3
0
        public void OnBanchoMatchStart(BanchoMatchStartArgs args)
        {
            if (args.Pr.ActiveMatch == null ||
                args.Pr.ActiveMatch.HostId != args.Pr.User.Id)
            {
                return;
            }

            Logger.Info(
                "%#FFFFFF% a %#f1fc5a%Multiplayer Room %#FFFFFF%called %#F94848%" +
                args.Pr.ActiveMatch.Name,
                "%#B342F4%(", args.Pr.ActiveMatch.MatchId, "%#B342F4%) %#FFFFFF%has started their Match!"
                );

            args.Pr.ActiveMatch.Start();
            args.Pr.ActiveMatch.Update();
        }
예제 #4
0
        public async Task<IActionResult> GetBeatmap(
            string map,
            [FromServices] Pisstaube pisstaube)
        {
            Logger.Debug(map);
            if (!Directory.Exists("data/beatmaps"))
                Directory.CreateDirectory("data/beatmaps");

            var beatmap = await pisstaube.DownloadBeatmapAsync(map, false);

            // No config reading for you :3
            map = map.Replace("..", string.Empty);
            if (!System.IO.File.Exists(beatmap))
                return NotFound($"Could not find Beatmap with the Name of {map}");

            return File(System.IO.File.OpenRead(beatmap), "text/html");
        }
예제 #5
0
파일: IRCServer.cs 프로젝트: Mempler/Sora
        public async void StartAsync()
        {
            await Task.Run(
                () =>
            {
                _listener.Start();
                var isCanceled = false;

                _token.Token.Register(
                    () => isCanceled = true
                    );

                while (!isCanceled)
                {
                    try
                    {
                        var client = _listener.AcceptTcpClient();

                        var ircClient = new IrcClient(client, _ctx, _cfg, _ps, _cs, _evmng, this);
                        lock (_conLocker)
                        {
                            _connectedClients.Add(ircClient);
                        }

                        new Thread(ircClient.Start).Start();
                    }
                    catch (Exception ex)
                    {
                        Logger.Err(ex);
                    }
                }

                lock (_conLocker)
                {
                    foreach (var c in _connectedClients)
                    {
                        c.Stop();
                    }
                }
            }
                );
        }
예제 #6
0
        public Task RunAsync()
        {
            BotPresence = new Presence
            {
                Status = new UserStatus
                {
                    Status          = Status.Watching,
                    Playmode        = PlayMode.Osu,
                    BeatmapChecksum = "nothing",
                    BeatmapId       = 0,
                    StatusText      = "over you!",
                    CurrentMods     = Mod.TouchDevice,
                },
                Info = new UserInformation
                {
                    Latitude         = 0,
                    Longitude        = 0,
                    ClientPermission = LoginPermissions.User |
                                       LoginPermissions.Administrator |
                                       LoginPermissions.Moderator |
                                       LoginPermissions.Supporter |
                                       LoginPermissions.TorneyStaff |
                                       LoginPermissions.BAT,
                    CountryId = CountryId.XX,
                    TimeZone  = 0,
                },
                ["BOT"] = true,
                ["IRC"] = true,
                User    = _dbUser,
            };


            _ps.Push(new PresenceSingle(BotPresence.User.Id));

            _ps.Join(BotPresence);

            Logger.Info("Hey, I'm Sora! I'm a bot and i say Hello World!");

            return(Task.CompletedTask);
        }
예제 #7
0
        public IPlugin GetEntryPoint(Assembly asm)
        {
            if (_entryPoints.ContainsKey(asm))
            {
                return(_entryPoints[asm]);
            }

            var iplug = asm.GetTypes()
                        .FirstOrDefault(
                x => x.IsClass &&
                (x.BaseType == typeof(IPlugin) ||
                 x.BaseType == typeof(Plugin))
                );

            if (iplug == null)
            {
                Logger.Warn("No entry point found for plugin", asm);
                _entryPoints.Add(asm, null);
                return(null);
            }

            var tArgs = (from cInfo in iplug.GetConstructors()
                         from pInfo in cInfo.GetParameters()
                         select _ev.Provider.GetService(pInfo.ParameterType)).ToArray();

            if (tArgs.Any(x => x == null))
            {
                throw new Exception("Could not find Dependency, are you sure you registered the Dependency?");
            }

            var retVal = Activator.CreateInstance(iplug, tArgs) as IPlugin;

            _entryPoints.Add(asm, retVal);

            return(retVal);
        }
예제 #8
0
        public void OnBroadcastFrames(BanchoMatchChangeSettingsArgs args)
        {
            if (args.Pr.ActiveMatch == null)
            {
                return;
            }
            if (args.Pr.ActiveMatch.HostId != args.Pr.User.Id)
            {
                return;
            }

            if (args.Pr.ActiveMatch.Name != args.Room.Name)
            {
                Logger.Info(
                    "%#F94848%" + args.Pr.User.UserName,
                    "%#B342F4%(", args.Pr.User.Id, "%#B342F4%)",
                    "%#FFFFFF% renamed a %#f1fc5a%Multiplayer Room %#FFFFFF%called %#F94848%" +
                    args.Pr.ActiveMatch.Name,
                    "%#B342F4%(", args.Room.MatchId, "%#B342F4%)",
                    "%#FFFFFF%and is now called %#F94848%" +
                    args.Room.Name,
                    "%#B342F4%(", args.Room.MatchId, "%#B342F4%)"
                    );
            }

            Logger.Info(
                "%#F94848%" + args.Pr.User.UserName,
                "%#B342F4%(", args.Pr.User.Id, "%#B342F4%)",
                "%#FFFFFF%changed the Settings of a %#f1fc5a%Multiplayer Room %#FFFFFF%called %#F94848%" +
                args.Room.Name,
                "%#B342F4%(", args.Room.MatchId, "%#B342F4%)"
                );

            args.Pr.ActiveMatch.ChangeSettings(args.Room);
            args.Pr.ActiveMatch.Update();
        }
예제 #9
0
        public async Task <IActionResult> IndexPost([FromHeader(Name = "osu-token")] string clientToken = null)
        {
            try
            {
                Response.Headers["cho-protocol"] = "19";
                Response.Headers["Connection"]   = "keep-alive";
                Response.Headers["Keep-Alive"]   = "timeout=60, max=100";
                Response.Headers["cho-server"]   = "Sora (https://github.com/Mempler/Sora)";

                Response.StatusCode = 200;

                await using var body = new MemoryStream();
                await Request.Body.CopyToAsync(body);

                body.Position = 0;

                await using var mw = MStreamWriter.New();
                using var mr       = new MStreamReader(body);
                var pr = new Presence();
                if (string.IsNullOrEmpty(clientToken))
                {
                    Response.Headers["cho-token"] = pr.Token.ToString();
                    string ip = Request.Headers["X-Real-IP"];

                    if (string.IsNullOrEmpty(ip))
                    {
                        ip = "127.0.0.1";
                    }

                    await _evManager.RunEvent(EventType.BanchoLoginRequest, new BanchoLoginRequestArgs
                    {
                        Reader    = mr,
                        Writer    = mw,
                        Pr        = pr,
                        IpAddress = ip,
                    });

                    mw.Flush();

                    return(await RetOut(mw.BaseStream));
                }

                if (_presenceService.TryGet(new Token(clientToken.Trim()), out pr))
                {
                    while (true)
                    {
                        try
                        {
                            pr["LAST_PONG"] = DateTime.Now;

                            if (Request.ContentLength - body.Position < 7)
                            {
                                break; // Dont handle any invalid packets! (less then bytelength of 7)
                            }
                            var packetId = (PacketId)mr.ReadInt16();
                            mr.ReadBoolean();
                            var packetData = mr.ReadBytes();

                            await using var packetDataStream = new MemoryStream(packetData);
                            using var packetDataReader       = new MStreamReader(packetDataStream);
                            await _evManager.RunEvent(
                                EventType.BanchoPacket,
                                new BanchoPacketArgs { Pr = pr, PacketId = packetId, Data = packetDataReader }
                                );
                        }
                        catch (Exception ex)
                        {
                            Logger.Err(ex);
                            break;
                        }
                    }

                    try
                    {
                        await using var m = new MemoryStream();
                        if (Response.Body.CanWrite)
                        {
                            pr.WritePackets(m);
                        }

                        return(await RetOut(m));
                    }
                    catch (Exception ex)
                    {
                        Logger.Err(ex);
                        // Ignored because it may throw an exception.
                    }
                }
                else
                {
                    return(StatusCode(403));
                }
            }
            catch (Exception ex)
            {
                Logger.Err(ex);
            }

            return(Ok());
        }
예제 #10
0
파일: Program.cs 프로젝트: Mempler/Sora
 private static void OnProcessExit(object sender, System.EventArgs e)
 {
     Logger.Info("Killing everything..");
     host?.Dispose();     // This is stupid. using statement ain't working...
     Environment.Exit(0); // we also have to call it manually ...
 }
예제 #11
0
        public async Task OnLoginRequest(BanchoLoginRequestArgs args)
        {
            try
            {
                var sw = new Stopwatch();
                sw.Start();

                var loginData = LoginParser.ParseLogin(args.Reader);
                if (loginData == null)
                {
                    Exception(args.Writer);
                    return;
                }

                var cacheKey = $"sora:user:{loginData.GetHashCode()}";

                if (!_cache.TryGet(cacheKey, out Presence presence))
                {
                    var dbUser = await DbUser.GetDbUser(_ctx, loginData.Username);

                    if (dbUser == null)
                    {
                        LoginFailed(args.Writer);
                        return;
                    }

                    if (!dbUser.IsPassword(loginData.Password))
                    {
                        _logger.Log(LogLevel.Warning, $"{LCOL.RED}{dbUser.UserName} " +
                                    $"{LCOL.PURPLE}({dbUser.Id}) " +
                                    $"{LCOL.RED}Failed {LCOL.WHITE}to Login!");
                        LoginFailed(args.Writer);
                        return;
                    }

                    if (args.IpAddress != "127.0.0.1" && args.IpAddress != "0.0.0.0")
                    {
                        try
                        {
                            var data = Localisation.GetData(args.IpAddress);

                            args.Pr.Info.Longitude = data.Location.Longitude ?? 0;
                            args.Pr.Info.Latitude  = data.Location.Latitude ?? 0;
                            args.Pr.Info.CountryId = Localisation.StringToCountryId(data.Country.IsoCode);
                        }
                        catch
                        {
                            // Ignored.. doesn't matter too much
                        }
                    }

                    args.Pr.User = dbUser;

                    args.Pr.Info.TimeZone = (byte)loginData.Timezone;

                    var lb = await DbLeaderboard.GetLeaderboardAsync(_ctx, dbUser);

                    args.Pr["LB"] = lb;

                    args.Pr.Stats.TotalScore        = lb.TotalScoreOsu;
                    args.Pr.Stats.RankedScore       = lb.RankedScoreOsu;
                    args.Pr.Stats.PerformancePoints = (ushort)lb.PerformancePointsOsu;
                    args.Pr.Stats.PlayCount         = (uint)lb.PlayCountOsu;
                    args.Pr.Stats.Accuracy          = (float)lb.GetAccuracy(_ctx, PlayMode.Osu);
                    args.Pr.Stats.Position          = lb.GetPosition(_ctx, PlayMode.Osu);

                    //args.pr["BLOCK_NON_FRIENDS_DM"] = loginData.BlockNonFriendDMs;

                    _cache.Set(cacheKey, args.Pr, TimeSpan.FromMinutes(30));
                }
                else
                {
                    var t = args.Pr.Token;
                    args.Pr       = presence;
                    args.Pr.Token = t;
                }

                if (_pcs.TryGet(args.Pr.User.Id, out var oldPresence))
                {
                    oldPresence.ActiveMatch?.Leave(args.Pr);
                    oldPresence.Spectator?.Leave(args.Pr);
                    _pcs.Leave(oldPresence);
                }

                _pcs.Join(args.Pr);

                Success(args.Writer, args.Pr.User.Id);

                args.Pr.Push(new ProtocolNegotiation());
                args.Pr.Push(new UserPresence(args.Pr));

                args.Pr.Push(new HandleUpdate(args.Pr));
                args.Pr.Info.ClientPermission = LoginPermissions.User;

                if (args.Pr.User.Permissions == Permission.GROUP_DONATOR)
                {
                    args.Pr.Info.ClientPermission |= LoginPermissions.Supporter;
                }
                if (args.Pr.User.Permissions == Permission.GROUP_ADMIN)
                {
                    args.Pr.Info.ClientPermission |=
                        LoginPermissions.BAT | LoginPermissions.Administrator | LoginPermissions.Moderator;
                }
                if (args.Pr.User.Permissions == Permission.GROUP_DEVELOPER)
                {
                    args.Pr.Info.ClientPermission |= LoginPermissions.Developer;
                }

                if (!args.Pr.User.Permissions.HasPermission(Permission.GROUP_DONATOR))
                {
                    if (_cfg.Server.FreeDirect)
                    {
                        args.Pr.Push(new LoginPermission(LoginPermissions.User | LoginPermissions.Supporter |
                                                         args.Pr.Info.ClientPermission));
                    }
                }
                else
                {
                    args.Pr.Push(new LoginPermission(args.Pr.Info.ClientPermission));
                }

                args.Pr.Push(new FriendsList(DbFriend.GetFriends(_ctx, args.Pr.User.Id).ToList()));
                args.Pr.Push(new PresenceBundle(_pcs.GetUserIds(args.Pr).ToList()));

                foreach (var chanAuto in _cs.ChannelsAutoJoin)
                {
                    if ((chanAuto.Status & ChannelStatus.AdminOnly) != 0 &&
                        args.Pr.User.Permissions == Permission.ADMIN_CHANNEL)
                    {
                        args.Pr.Push(new ChannelAvailableAutojoin(chanAuto));
                    }
                    else if ((chanAuto.Status & ChannelStatus.AdminOnly) == 0)
                    {
                        args.Pr.Push(new ChannelAvailableAutojoin(chanAuto));
                    }

                    args.Pr.Push(new ChannelJoinSuccess(chanAuto));
                    chanAuto.Join(args.Pr);
                }

                foreach (var channel in _cs.Channels)
                {
                    if ((channel.Status & ChannelStatus.AdminOnly) != 0 &&
                        args.Pr.User.Permissions == Permission.ADMIN_CHANNEL)
                    {
                        args.Pr.Push(new ChannelAvailable(channel));
                    }
                    else if ((channel.Status & ChannelStatus.AdminOnly) == 0)
                    {
                        args.Pr.Push(new ChannelAvailable(channel));
                    }
                }

                _pcs.Push(new PresenceSingle(args.Pr.User.Id));
                _pcs.Join(args.Pr);

                args.Pr.WritePackets(args.Writer.BaseStream);

                sw.Stop();
                _logger.Log(LogLevel.Debug, "Login Time:\nMS: ", sw.Elapsed.TotalMilliseconds);

                _logger.Log(LogLevel.Information,
                            $"{LCOL.RED}{args.Pr.User.UserName} {LCOL.PURPLE}( {args.Pr.User.Id} ) {LCOL.WHITE}has logged in!"
                            );

                args.Pr["LAST_PONG"] = DateTime.Now;
            }
            catch (Exception ex)
            {
                Logger.Err(ex);
                Exception(args.Writer);
            }
        }
예제 #12
0
        public async Task <IActionResult> GetScoreResult(
            [FromQuery(Name = "v")] ScoreboardType type,
            [FromQuery(Name = "c")] string fileMd5,
            [FromQuery(Name = "f")] string f,
            [FromQuery(Name = "m")] PlayMode playMode,
            [FromQuery(Name = "i")] int i,
            [FromQuery(Name = "mods")] Mod mods,
            [FromQuery(Name = "us")] string us,
            [FromQuery(Name = "ha")] string pa,
            [FromServices] IServiceProvider serviceProvider,
            [FromServices] SoraDbContext ctx,
            [FromServices] DbContextPool <SoraDbContext> ctxPool,
            [FromServices] Pisstaube pisstaube,
            [FromServices] Cache cache)
        {
            try
            {
                var dbUser = await DbUser.GetDbUser(ctx, us);

                if (dbUser?.IsPassword(pa) != true)
                {
                    return(Ok("error: pass"));
                }

                var cacheHash =
                    Hex.ToHex(
                        Crypto.GetMd5(
                            $"{fileMd5}{playMode}{mods}{type}{dbUser.Id}{dbUser.UserName}"
                            )
                        );

                if (cache.TryGet($"sora:Scoreboards:{cacheHash}", out string cachedData))
                {
                    return(Ok(cachedData));
                }

                var scores = DbScore.GetScores(ctx, fileMd5, dbUser, playMode,
                                               type == ScoreboardType.Friends,
                                               type == ScoreboardType.Country,
                                               type == ScoreboardType.Mods,
                                               mods);

                BeatmapSet set      = null;
                DbScore    ownScore = null;

                var beatmap = DbBeatmap.GetBeatmap(ctx, fileMd5);
                if (beatmap == null)
                {
                    var apiSet = await pisstaube.FetchBeatmapSetAsync(fileMd5);

                    if (apiSet == null)
                    {
                        goto JustContinue;
                    }

                    var beatmaps         = DbBeatmap.FromBeatmapSet(apiSet).ToList();
                    var beatmapChecksums = beatmaps.Select(s => s.FileMd5);
                    var dbBeatmaps       =
                        ctx.Beatmaps.Where(rset => beatmapChecksums.Any(lFileMd5 => rset.FileMd5 == lFileMd5))
                        .ToList();

                    var pool = serviceProvider.GetRequiredService <DbContextPool <SoraDbContext> >();
                    Task.WaitAll(beatmaps.Select(rawBeatmap => Task.Run(async() =>
                    {
                        var context = pool.Rent();

                        try
                        {
                            var dbBeatmap = dbBeatmaps.FirstOrDefault(s => s.FileMd5 == rawBeatmap.FileMd5);

                            if (dbBeatmap != null && (dbBeatmap.Flags & DbBeatmapFlags.RankedFreeze) != 0)
                            {
                                rawBeatmap.RankedStatus = dbBeatmap.RankedStatus;
                                rawBeatmap.Flags        = dbBeatmap.Flags;
                            }

                            context.Beatmaps.AddOrUpdate(rawBeatmap);
                            await context.SaveChangesAsync();
                        }
                        finally
                        {
                            pool.Return(context);
                        }
                    })).ToArray());

                    beatmap = beatmaps.FirstOrDefault(s => s.FileMd5 == fileMd5);
                }

                await foreach (var score in DbScore.GetScores(ctx, fileMd5, dbUser, playMode, false, false, false, mods, true))
                {
                    ownScore = score;
                    break;
                }

                if (beatmap != null)
                {
                    set = new BeatmapSet
                    {
                        SetID        = beatmap.Id,
                        Artist       = beatmap.Artist,
                        Title        = beatmap.Title,
                        RankedStatus = beatmap.RankedStatus,

                        ChildrenBeatmaps = new List <Beatmap>
                        {
                            new Beatmap
                            {
                                FileMD5     = beatmap.FileMd5,
                                DiffName    = beatmap.DiffName,
                                ParentSetID = beatmap.SetId,
                                BeatmapID   = beatmap.Id,
                                Mode        = beatmap.PlayMode,
                            },
                        },
                    }
                }
                ;

JustContinue:
                var sboard = new Scoreboard(set?.ChildrenBeatmaps.FirstOrDefault(bm => bm.FileMD5 == fileMd5),
                                            set, scores, ownScore);

                cache.Set($"sora:Scoreboards:{cacheHash}", cachedData = await sboard.ToOsuString(ctxPool), TimeSpan.FromSeconds(30));
                return(Ok(cachedData));
            }
            catch (Exception ex)
            {
                Logger.Err(ex);
                return(Ok("Failed"));
            }
        }
예제 #13
0
        public ConsoleCommandService(SoraDbContext ctx)
        {
            Commands = new List <ConsoleCommand>();
            _mut     = new object();

            #region DefaultCommands

            RegisterCommand(
                "help",
                "Get a list of ALL Commands!",
                new List <Argument> {
                new Argument {
                    ArgName = "command"
                }
            },
                0,
                args =>
            {
                var l = "\n====== Command List ======\n";

                foreach (var cmd in Commands)
                {
                    var aList = "\t<";

                    var i = 0;
                    foreach (var a in cmd.Args)
                    {
                        aList += a.ArgName;
                        if (i >= cmd.ExpectedArgs)
                        {
                            aList += "?";
                        }
                        aList += ", ";
                        i++;
                    }

                    aList = aList.TrimEnd(cmd.Args.Count < 1 ? '<' : ' ').TrimEnd(',');
                    if (cmd.Args.Count > 0)
                    {
                        aList += ">";
                    }

                    l += "\n%#1c9624%/*\n";
                    l += cmd.Description;
                    l += $"\n*/{LCol.WHITE}\n";
                    l += "\n" + cmd.Command + aList;
                    l += "\n";
                }

                l += "\n==========================";

                Logger.Info(l);

                return(true);
            }
                );

            RegisterCommand(
                "clear",
                "Clear Console",
                new List <Argument>(),
                0,
                args =>
            {
                Console.Clear();
                return(true);
            }
                );

            RegisterCommand(
                "adduser",
                "Add a User to our Database",
                new List <Argument>
            {
                new Argument {
                    ArgName = "Username"
                },
                new Argument {
                    ArgName = "Password"
                },
                new Argument {
                    ArgName = "E-Mail"
                },
                new Argument {
                    ArgName = "Permissions"
                },
            },
                3,
                args =>
            {
                var u = DbUser.RegisterUser(ctx, Permission.From(args[3..].Join()),
                                            args[0], args[2], args[1], false).Result;

                if (u == null)
                {
                    Logger.Err("Failed! User has already been registered!");
                    return(true);
                }


                Logger.Info(args[1]);
                Logger.Info(
                    "Created User",
                    "%#F94848%" + u.UserName,
                    "%#B342F4%(", u.Id, "%#B342F4%)"
                    );
                return(true);
            }
예제 #14
0
파일: IRCClient.cs 프로젝트: Mempler/Sora
        public async Task Receive()
        {
            var stream = _client.GetStream();
            var bytes  = new byte[4096];

            _writer = new StreamWriter(stream);

            // TODO: Finish http://www.networksorcery.com/enp/protocol/irc.htm
            try
            {
                int i;
                while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                {
                    if (_exit)
                    {
                        return;
                    }

                    var data = Encoding.UTF8.GetString(bytes, 0, i).Split("\n");

                    foreach (var cmd in data.Select(d => d.Split(" "))
                             .Where(cmd => !string.IsNullOrEmpty(cmd[0]))
                             .Where(cmd => !string.IsNullOrWhiteSpace(cmd[0])))
                    {
                        switch (cmd[0])
                        {
                        case "USER":
                            if (_authorized)
                            {
                                SendCodeMessage(462, "Unauthorized command (already authorized!)");
                                continue;
                            }

                            if (cmd.Length < 2)
                            {
                                SendCodeMessage(451, "You have not registered");
                                continue;
                            }

                            _nickname = cmd[1].Trim();
                            var rUser = DbUser.GetDbUser(_ctx, cmd[1].Trim()).Result;
                            if (rUser == null)
                            {
                                SendCodeMessage(451, "You have not registered");
                                continue;
                            }

                            _user = rUser;

                            if (_authorized)
                            {
                                SendCodeMessage(462, "Unauthorized command (already authorized!)");
                                continue;
                            }

                            if (rUser?.IsPassword(Hex.ToHex(Crypto.GetMd5(_pass.Trim()))) != true)
                            {
                                if (string.IsNullOrEmpty(_nickname))
                                {
                                    _nickname = "anonymous";
                                }

                                Logger.Info(_pass);

                                SendCodeMessage(464, "Password incorrect");
                                continue;
                            }

                            _presence      = new Presence();
                            _presence.User = _user;

                            _presence["STATUS"] = new UserStatus();
                            _presence["IRC"]    = true;

                            WriteConnectionResponse();
                            WriteStatus();
                            WriteMotd();

                            _authorized = true;
                            break;

                        case "JOIN":
                            if (_presence == null || !_authorized)
                            {
                                SendCodeMessage(444, "You're not logged in");
                                break;
                            }

                            if (_cs.TryGet(cmd[1].Trim(), out var channel))
                            {
                                SendCodeMessage(403, $"{cmd[1]}: No such Channel!");
                                break;
                            }

                            channel.Join(_presence);

                            /*
                             * if (!)
                             * {
                             *  SendCodeMessage(482, "You're not allowed to Join this Channel!");
                             *  break;
                             * }
                             */

                            if (channel.Topic == "")
                            {
                                SendCodeMessage(331, "No topic is set", channel: channel.Name);
                            }
                            else
                            {
                                SendCodeMessage(332, channel.Topic, channel: channel.Name);
                            }

                            var uList = string.Join(
                                " ", channel.Presences
                                .Select(
                                    ps =>
                            {
                                var ret = ps.User.UserName.Replace(" ", "_");

                                if (ps.User.Permissions.HasPermission(Permission.GROUP_CHAT_MOD))
                                {
                                    ret = "@" + ret;
                                }
                                else if (ps.User.Permissions.HasPermission(
                                             Permission.GROUP_DONATOR
                                             ))
                                {
                                    ret = "+" + ret;
                                }

                                return(ret);
                            })
                                .ToArray()
                                );
                            uList += " @olSora";
                            SendCodeMessage(353, $"{uList}", channel: "= " + channel.Name);
                            SendCodeMessage(366, "End of NAMES list", channel: channel.Name);
                            break;

                        case "PING":
                            if (_presence == null || !_authorized)
                            {
                                SendCodeMessage(444, "You're not logged in");
                                break;
                            }

                            await _evmng.RunEvent(EventType.BanchoPong, new BanchoPongArgs { Pr = _presence });

                            SendMessage($"PONG {_cfg.Server.Hostname}: {cmd[1]}");
                            break;

                        case "PASS":
                            _pass = cmd[1];
                            break;

                        case "QUIT":
                            _client.Close();
                            _parent.RemoveTcpClient(this);
                            return;

                        // Ignored Commands
                        case "NICK":
                        case "CAP":
                            break;

                        default:
                            Logger.Debug(string.Join(" ", cmd));
                            if (!_authorized)
                            {
                                SendCodeMessage(444, "You're not logged in");
                            }
                            break;
                        }
                    }

                    _writer.Flush();
                }
            }
            catch (Exception e)
            {
                Logger.Err(e);
                _client.Close();
            }
        }