using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using Chop.Database; using System.Data; using System.Threading; namespace Chop { public class GameClient { public static uint MESSAGEID_MAX = 2002; //public static string POLICY = " "; private uint mID; private Socket mSocket; private byte[] mBuffer; private bool mPonged; private Request mRequest; private Response mResponse; private Habbo mHabbo; private delegate void requestHandler(); private requestHandler[] mRequestHandlers; public uint ID { get { return mID; } } public string IP { get { return mSocket.RemoteEndPoint.ToString().Split(':')[0]; } } public bool Ponged { get { return mPonged; } set { mPonged = value; } } public GameClient(uint ID, Socket Socket) { this.mID = ID; this.mSocket = Socket; this.mPonged = true; this.mBuffer = new byte[1024]; this.mRequestHandlers = new requestHandler[MESSAGEID_MAX + 1]; this.mResponse = new Response(); mSocket.BeginReceive(mBuffer, 0, mBuffer.Length, SocketFlags.None, new AsyncCallback(DataIn), null); RegisterGlobal(); } private void DataIn(IAsyncResult iAr) { try { int bytesIn = mSocket.EndReceive(iAr); if (bytesIn > 0) { string data = Encoding.Default.GetString(mBuffer, 0, bytesIn); while (data.Length > 0) { string cData = data.Substring(3, HabboEncoding.decodeB64(data.Substring(1, 2))); string consoleText = cData; // We want to replace some chars with readable text! for (int i = 0; i < 13; i++) // char 0-13 { consoleText = consoleText.Replace(Convert.ToChar(i).ToString(), "{" + i + "}"); } Console.WriteLine("Incoming data from client [" + mID + "]: \"" + consoleText + "\""); mRequest = Request.Parse(cData); if (mRequest.HeaderID <= MESSAGEID_MAX) if (mRequestHandlers[mRequest.HeaderID] != null) mRequestHandlers[mRequest.HeaderID].Invoke(); else { Console.WriteLine("Message ID [" + mRequest.HeaderID + "] is not registered"); // Not registered } else { Console.WriteLine("Message ID [" + mRequest.HeaderID + "] is over the max message ID"); // Over max ID } data = data.Substring(cData.Length + 3); } } else { Core.ClientManager.RemoveClient(mID); return; } } catch (SocketException) // Nothing special { Core.ClientManager.RemoveClient(mID); } catch (ObjectDisposedException) // Nothing special { Core.ClientManager.RemoveClient(mID); } catch (Exception x) { Console.WriteLine(x.ToString() + " (GameClient.DataIn)"); Core.ClientManager.RemoveClient(mID); } finally { if (mSocket.Connected) mSocket.BeginReceive(mBuffer, 0, mBuffer.Length, SocketFlags.None, new AsyncCallback(DataIn), null); } } public void SendData(string data) { try { string consoleText = data; // We want to replace some chars with readable text! for (int i = 0; i < 13; i++) // char 0-13 { consoleText = consoleText.Replace(Convert.ToChar(i).ToString(), "{" + i + "}"); } Console.WriteLine("Outgoing data to client [" + mID + "]: \"" + consoleText + "\""); mSocket.Send(Encoding.Default.GetBytes(data)); } catch (SocketException) // Nothing special { Core.ClientManager.RemoveClient(mID); } catch (Exception x) { Console.WriteLine(x.ToString() + " (GameClient.SendData)"); Core.ClientManager.RemoveClient(mID); } } private void SendResponse() { SendMessage(mResponse); } public void SendMessage(Response Message) { SendData(Message.ToString() + Convert.ToChar(1)); } internal void Destroy() { if (mSocket.Connected) { mSocket.Shutdown(SocketShutdown.Both); mSocket.Close(); mSocket.Dispose(); } } private void RegisterGlobal() { mRequestHandlers[196] = new requestHandler(PONG); mRequestHandlers[202] = new requestHandler(GENERATEKEY); mRequestHandlers[206] = new requestHandler(INIT_CRYPTO); mRequestHandlers[49] = new requestHandler(GDATE); mRequestHandlers[42] = new requestHandler(APPROVENAME); mRequestHandlers[203] = new requestHandler(APPROVE_PASSWORD); mRequestHandlers[197] = new requestHandler(APPROVEEMAIL); mRequestHandlers[46] = new requestHandler(AC); mRequestHandlers[131] = new requestHandler(COPPA_REG_GETREALTIME); mRequestHandlers[43] = new requestHandler(REGISTER); mRequestHandlers[4] = new requestHandler(TRY_LOGIN); mRequestHandlers[7] = new requestHandler(GET_INFO); mRequestHandlers[8] = new requestHandler(GET_CREDITS); } /// /// 196 - "CD" - "PONG" /// private void PONG() { mPonged = true; } /// /// 206 - "CN" - "INIT_CRYPTO" /// private void INIT_CRYPTO() { mResponse.Init(277); // "DU" mResponse.AppendWired(0); SendResponse(); } /// /// 202 - "CJ" - "GENERATEKEY" /// private void GENERATEKEY() { mResponse.Init(8); // "@H" mResponse.Append("[100,105,110,115,120,125,130,135,140,145,150,155,160,165,170,175,176,177,178,180,185,190,195,200,205,206,207,210,215,220,225,230,235,240,245,250,255,260,265,266,267,270,275,280,281,285,290,295,300,305,500,505,510,515,520,525,530,535,540,545,550,555,565,570,575,580,585,590,595,596,600,605,610,615,620,625,626,627,630,635,640,645,650,655,660,665,667,669,670,675,680,685,690,695,696,700,705,710,715,720,725,730,735,740,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873]"); SendResponse(); mResponse.Init(257); // "DA" mResponse.AppendWired(6); mResponse.AppendWired(0); mResponse.AppendWired(1); mResponse.AppendWired(1); mResponse.AppendWired(1); mResponse.AppendWired(3); mResponse.AppendWired(0); mResponse.AppendWired(2); mResponse.AppendWired(1); mResponse.AppendWired(4); mResponse.AppendWired(1); mResponse.AppendWired(5); mResponse.AppendString(Core.Config.GetString("core.date.format")); // Date format SendResponse(); } /// /// 49 - "@q" - "GDATE" /// private void GDATE() { mResponse.Init(163); // "Bc" mResponse.Append(DateTime.Today.ToString(Core.Config.GetString("core.date.format"))); SendResponse(); } /// /// 42 - "@j" - "APPROVENAME" /// private void APPROVENAME() { // Errors: // 0 = valid // 1 = too long // 2 = unacceptable to staff // 3 = 2 (unacceptable to staff) // 4 = name taken string Name = mRequest.PopString(); int ErrorID = 0; if (Name.Length > 15) // Name too long ErrorID = 1; if (Name.Length < 2) // Name too short ErrorID = 2; else if (Name.Contains("MOD-") || StringHelper.ContainsOtherChars(Name, Core.Config.GetString("game.registration.characters.allowed"))) // invalid name ErrorID = 3; else if (false) // name taken ErrorID = 4; mResponse.Init(36); // "@d" mResponse.AppendWired(ErrorID); SendResponse(); } /// /// 203 - "CK" - "APPROVE_PASSWORD" /// private void APPROVE_PASSWORD() { // Errors: // 0 = valid // 1 = too short // 2 = too long // 3 = invalid chars // 4 = needs atleast 1 number // 5 = name & password too similar // must be 6 chr long // only chars = 1234567890qwertyuiopasdfg hjklzxcvbnm-=?!@@:., string Name = mRequest.PopString(); string Password = mRequest.PopString(); int ErrorID = 0; if (Password.Length < 6) ErrorID = 1; else if (Password.Length > 10) ErrorID = 2; else if (StringHelper.ContainsOtherChars(Password, Core.Config.GetString("game.registration.characters.allowed"))) ErrorID = 3; else if (!StringHelper.ContainsNumber(Password)) ErrorID = 4; else if (Password.Contains(Name)) ErrorID = 5; mResponse.Init(282); // "DZ" mResponse.AppendWired(ErrorID); SendResponse(); } /// /// 197 - "CK" - "APPROVEEMAIL" /// private void APPROVEEMAIL() { string Mail = mRequest.PopString(); if (Mail.Contains('@') && Mail.Contains('.')) // TODO: Maybe better check? mResponse.Init(271); // "DO" else mResponse.Init(272); // "DP" SendResponse(); } /// /// 46 - "@n" - "AC" /// private void AC() { try { DateTime Birth = DateTime.Parse(mRequest.Content, new System.Globalization.CultureInfo("en-GB")); if (Birth.Year > DateTime.Now.Year - Core.Config.GetInt("game.registration.age.min")) mResponse.Init(272); // "DP" else if (Birth.Year < DateTime.Now.Year - Core.Config.GetInt("game.registration.age.max")) mResponse.Init(272); // "DP" else mResponse.Init(271); // "DO" } catch { mResponse.Init(272); // "DP" } SendResponse(); } /// /// 131 - "BC" - "COPPA_REG_GETREALTIME" /// private void COPPA_REG_GETREALTIME() { mResponse.Init(214); // "CV" SendResponse(); } /// /// 43 - "@k" - "REGISTER" /// private void REGISTER() { #region Decoding //newUser.sPassword = FuseMessage.getStructured(3, mPacketBody); //newUser.sFigure = FuseMessage.getStructured(4, mPacketBody); //newUser.sSex = FuseMessage.getStructured(5, mPacketBody); //newUser.sMission = FuseMessage.getStructured(6, mPacketBody); //newUser.sEmail = FuseMessage.getStructured(7, mPacketBody); //newUser.sDOB = FuseMessage.getStructured(8, mPacketBody); //mRequest.Content = mRequest.Content.Replace("A@", "@E"); string Name = "MOD-|"; // mRequest.GetStructured(2); string Figure = "f"; // mRequest.GetStructured(4); char Gender = 'g'; // Convert.ToChar(mRequest.GetStructured(5)); string Mail = "m"; // mRequest.GetStructured(7); string Birth = "b"; // mRequest.GetStructured(8); string Password = "p"; // mRequest.GetStructured(3); bool ReceiveMails = false; for (int i = 0; i < 6; i++) { int ID = mRequest.PopBase64Int(); string Content = mRequest.PopString(); switch (ID) { case 2: Name = Content; break; case 4: Figure = Content; break; case 5: Gender = Convert.ToChar(Content); break; case 6: // Unknown, maybe parent mail? break; case 7: Mail = Content; break; case 8: Birth = Content; break; } } // Get rid of trash mRequest.PopBase64Int(); mRequest.PopBase64Int(); mRequest.PopBase64Int(); mRequest.PopBase64Int(); ReceiveMails = mRequest.PopBase64Boolean(); // More trash... mRequest.PopBase64Int(); Password = mRequest.PopString(); #endregion #region Checking bool Error = false; if (Name.Length > 15) // Name too long Error = true; if (Name.Length < 2) // Name too short Error = true; else if (Name.Contains("MOD-") || StringHelper.ContainsOtherChars(Name, Core.Config.GetString("game.registration.characters.allowed"))) // invalid name return; else if (false) // name taken Error = true; if (Password.Length < 6) return; else if (Password.Length > 10) Error = true; else if (StringHelper.ContainsOtherChars(Password, Core.Config.GetString("game.registration.characters.allowed"))) Error = true; else if (!StringHelper.ContainsNumber(Password)) Error = true; else if (Password.Contains(Name)) Error = true; try { DateTime BirthTime = DateTime.Parse(Birth, new System.Globalization.CultureInfo("en-GB")); if (BirthTime.Year > DateTime.Now.Year - Core.Config.GetInt("game.registration.age.min")) Error = true; else if (BirthTime.Year < DateTime.Now.Year - Core.Config.GetInt("game.registration.age.max")) Error = true; } catch { Error = true; } if (!(Mail.Contains('@') && Mail.Contains('.'))) // TODO: Maybe better check? Error = true; if (Gender != 'M' && Gender != 'F') Error = true; // TODO: Figure check if (Error) { Core.ClientManager.RemoveClient(ID); return; } #endregion #region Saving using (DatabaseClient dbClient = Core.DatabaseManager.GetClient()) { dbClient.AddParamWithValue("name", Name); dbClient.AddParamWithValue("figure", Figure); dbClient.AddParamWithValue("gender", Gender); dbClient.AddParamWithValue("mail", Mail); dbClient.AddParamWithValue("birth", Birth); dbClient.AddParamWithValue("password", Password); dbClient.AddParamWithValue("credits", Core.Config.GetInt("game.registration.default.credits")); dbClient.AddParamWithValue("motto", Core.Config.GetString("game.registration.default.motto")); dbClient.ExecuteQuery("INSERT INTO habbos(name,figure,gender,mail,birth,password,credits,motto) VALUES(@name,@figure,@gender,@mail,@birth,@password,@credits,@motto)"); } #endregion } /// /// 4 - "@D" - "TRY_LOGIN" /// private void TRY_LOGIN() { string Username = mRequest.PopString(); string Password = mRequest.PopString(); DataRow row; using (DatabaseClient dbClient = Core.DatabaseManager.GetClient()) { dbClient.AddParamWithValue("username", Username); row = dbClient.ReadDataRow("SELECT * FROM habbos WHERE name = @username"); } if (row == null) // User don't exists! { mResponse.Init(33); // "@a" mResponse.Append("login incorrect: Wrong username"); SendResponse(); return; } else if (row["password"].ToString() != Password) // Wrong password! { mResponse.Init(33); // "@a" mResponse.Append("login incorrect: Wrong password"); SendResponse(); return; } DataRow banRow; using (DatabaseClient dbClient = Core.DatabaseManager.GetClient()) { dbClient.AddParamWithValue("id", row["id"]); banRow = dbClient.ReadDataRow("SELECT * FROM bans WHERE habboid = @id"); } if (banRow != null) // ban fond { int i = DateTime.Compare(DateTime.Parse(banRow["expires"].ToString(), new System.Globalization.CultureInfo("en-GB")), DateTime.Now); if (i < 0) // Ban expired using (DatabaseClient dbClient = Core.DatabaseManager.GetClient()) { dbClient.AddParamWithValue("id", row["id"]); dbClient.ExecuteQuery("DELETE FROM bans WHERE habboid = @id"); } else { mResponse.Init(35); // "@c" mResponse.Append(banRow["message"]); SendResponse(); Thread.Sleep(10000); Core.ClientManager.RemoveClient(ID); return; } } mHabbo = Habbo.Parse(row, this); mResponse.Init(2); // "@B" mResponse.AppendString("fuse_default"); SendResponse(); mResponse.Init(3); // "@C" SendResponse(); mResponse.Init(8); // "@H" mResponse.Append("[100,105,110,115,120,125,130,135,140,145,150,155,160,165,170,175,176,177,178,180,185,190,195,200,205,206,207,210,215,220,225,230,235,240,245,250,255,260,265,266,267,270,275,280,281,285,290,295,300,305,500,505,510,515,520,525,530,535,540,545,550,555,565,570,575,580,585,590,595,596,600,605,610,615,620,625,626,627,630,635,640,645,650,655,660,665,667,669,670,675,680,685,690,695,696,700,705,710,715,720,725,730,735,740,800,801,802,803,804,805,806,807,808,809,810,811,812,813,814,815,816,817,818,819,820,821,822,823,824,825,826,827,828,829,830,831,832,833,834,835,836,837,838,839,840,841,842,843,844,845,846,847,848,849,850,851,852,853,854,855,856,857,858,859,860,861,862,863,864,865,866,867,868,869,870,871,872,873]"); SendResponse(); } /// /// 7 - "@G" - "GET_INFO" /// private void GET_INFO() { mResponse.Init(5); // "@E" mResponse.Append(mHabbo.ToFuseString()); SendResponse(); } /// /// 8 - "@H" - "GET_CREDITS" /// private void GET_CREDITS() { mResponse.Init(6); // "@F" mResponse.Append(mHabbo.Credits + ".0"); SendResponse(); } } }