using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using log4net;
using Mango.Communication.Packets.Incoming;
using Mango.Players;
using Mango.Utilities;
using Mango.Communication.Packets.Outgoing;
using Mango.Communication.Packets.Outgoing.Handshake;
using Mango.Communication.Packets.Outgoing.Availability;
using Mango.Communication.Packets.Outgoing.Notifications;
using Mango.Communication.Packets.Outgoing.Inventory.Purse;
using System.Collections.Concurrent;
using System.Threading;
using Mango.Communication.Packets.Outgoing.Users;
using Mango.Subscriptions;
using Mango.Communication.Packets.Outgoing.Moderation;
using Mango.Communication.Packets.Outgoing.Inventory.AvatarEffects;
using Mango.Communication.Encryption;
using Mango.Communication.Packets.Outgoing.Navigator;
using Mango.Communication.Packets.Outgoing.Room.Session;
using MySql.Data.MySqlClient;
using Mango.Database.Exceptions;
namespace Mango.Communication.Sessions
{
sealed class Session
{
private static readonly ILog log = LogManager.GetLogger("Mango.Communication.Sessions.Session");
///
/// Unique ID which indentifies this Session. (It may only be used for debugging purposes)
///
public int Id { get; private set; }
///
/// The Socket used for the backend of the communication.
///
public Socket Socket { get; set; }
///
/// The manager that was used to create this session.
///
public ServerSocket Manager { get; private set; }
public SocketAsyncEventArgs SendEventArgs { get; set; }
private readonly ConcurrentQueue SendQueue = new ConcurrentQueue();
private int WriterCount = 0;
//private EventWaitHandle DisconnectWait = new EventWaitHandle(false, EventResetMode.AutoReset);
private AutoResetEvent DisconnectWait = new AutoResetEvent(true);
public int DesignedHandler { get; set; }
public RC4 RC4Client = null;
public bool CryptoInitialized { get; set; }
public string MachineId { get; set; }
///
/// The game player linked to this active session.
///
private Player Player = null;
private SessionPacketHandler PacketHandler = null;
///
/// Initializes a new instance of the Session class.
///
/// The manager which created this session.
public Session(int id, ServerSocket Manager, SocketAsyncEventArgs sendEventArgs)
{
this.Id = id;
this.Manager = Manager;
this.SendEventArgs = sendEventArgs;
this.PacketHandler = new SessionPacketHandler();
}
///
/// Gets the IP Address of this connection session.
///
public string IPAddress
{
get
{
return this.Socket.RemoteEndPoint.ToString().Split(':')[0];
}
}
bool HalfDataRcv = false;
byte[] HalfData = null;
public void OnReceiveData(byte[] Data)
{
if (this.DisconnectedCalled) // stops any packets from processing
{
return;
}
this.CryptoInitialized = false;
if (HalfDataRcv)
{
byte[] FullDataRcv = new byte[HalfData.Length + Data.Length];
Buffer.BlockCopy(HalfData, 0, FullDataRcv, 0, HalfData.Length);
Buffer.BlockCopy(Data, 0, FullDataRcv, HalfData.Length, Data.Length);
HalfDataRcv = false; // mark done this round
OnReceiveData(FullDataRcv); // repeat now we have the combined array
return;
}
if (Data[0] == 60)
{
string Policy = "\r\n"
+ "\r\n"
+ "\r\n"
+ "\r\n"
+ "\r\n"
+ "";
SendData(Policy);
Disconnect();
}
else if (Data[0] != 67)
{
try // SO LAZYYYYYYYYY NEED FIX THIS OMG :(
{
using (BinaryReader Reader = new BinaryReader(new MemoryStream(Data)))
{
int MsgLen = HabboEncoding.DecodeInt32(this.CryptoInitialized ? this.RC4Client.Parse(Reader.ReadBytes(4)) : Reader.ReadBytes(4));
if ((Reader.BaseStream.Length - 4) < MsgLen) // was 3
{
HalfData = Data;
HalfDataRcv = true;
return;
}
byte[] Content = this.CryptoInitialized ? this.RC4Client.Parse(Reader.ReadBytes(MsgLen)) : Reader.ReadBytes(MsgLen);
using (BinaryReader SecondaryReader = new BinaryReader(new MemoryStream(Content)))
{
int MsgId = HabboEncoding.DecodeInt16(SecondaryReader.ReadBytes(2));
byte[] ReadableContent = new byte[(Content.Length - 2)];
Buffer.BlockCopy(Content, 2, ReadableContent, 0, (Content.Length - 2));
ClientPacket Packet = ClientPacketFactory.CreateNew();
Packet.Initialize(MsgId, ReadableContent);
OnPacketReady(Packet);
}
if ((Reader.BaseStream.Length - 4) > MsgLen)
{
byte[] MoreContent = new byte[Reader.BaseStream.Length - Reader.BaseStream.Position];
Buffer.BlockCopy(Data, (int)Reader.BaseStream.Position, MoreContent, 0, (int)(Reader.BaseStream.Length - Reader.BaseStream.Position));
OnReceiveData(MoreContent);
}
}
}
catch { Disconnect(); }
}
}
///
/// This method is called for when a packet is ready to be read.
///
///
private void OnPacketReady(ClientPacket Packet)
{
this.PacketHandler.ExecutePacket(this, Packet);
}
///
/// This method is called when the Session has disconnected.
///
public void OnDisconnection()
{
if (this.Player != null)
{
this.Player.Cleanup();
this.Player = null;
}
this.DesignedHandler = 1;
this.RC4Client = null;
this.CryptoInitialized = false;
this.HalfData = null;
this.HalfDataRcv = false;
this.MachineId = string.Empty;
this.PacketHandler.Reset();
this.DisconnectedCalled = false; // reset this value!!!!
this.DisconnectWait.Set();
}
private bool DisconnectedCalled = false;
///
/// Forces this player to be disconnected
///
public void Disconnect()
{
if (!this.DisconnectedCalled)
{
this.DisconnectedCalled = true;
this.Socket.Shutdown(SocketShutdown.Both);
this.DisconnectWait.WaitOne();
}
}
public void SendPacket(ServerPacket packet)
{
this.SendData(packet.GetBytes());
}
public void SendData(string Data)
{
SendData(Encoding.UTF8.GetBytes(Data));
}
public void SendData(byte[] data)
{
QueueToSend(data);
}
public void QueueToSend(byte[] data)
{
this.SendQueue.Enqueue(data);
PromptToSend();
}
public void PromptToSend()
{
if (Interlocked.CompareExchange(ref WriterCount, 1, 0) == 0)
{
StartSending();
}
}
public void StartSending()
{
byte[] result = null;
if (this.SendQueue.TryDequeue(out result))
{
SendDataToken token = (SendDataToken)SendEventArgs.UserToken;
token.DataToSend = result;
token.SendBytesRemainingCount = result.Length;
this.Manager.BeginSend(SendEventArgs);
}
else
{
Interlocked.Exchange(ref WriterCount, 0);
}
}
public void OnSendCompleted()
{
StartSending();
}
public void TryAuthenticate(string SSOTicket)
{
PlayerData Data = null;
if (!SSOAuthenticator.TryAuthenticate(SSOTicket, IPAddress, out Data))
{
Disconnect();
return;
}
Player CurrentPlayer = null;
if (Mango.GetServer().GetPlayerManager().TryGet(Data.Id, out CurrentPlayer))
{
CurrentPlayer.GetSession().Disconnect();
}
Player Player = new Player(this, Data);
SetPlayer(Player);
if (!Player.InitializeComponents())
{
Disconnect();
return;
}
// perform this last
if (!Mango.GetServer().GetPlayerManager().TryAdd(Player))
{
Disconnect(); // player manager told us something went wrong.. remove this player
return;
}
// tell client we are all ok
SendPacket(new AuthenticationOKComposer());
SendPacket(new AvailabilityStatusComposer());
SendPacket(new NavigatorSettingsComposer(Player.HomeRoom));
SendPacket(new UserRightsComposer(Player.GetPermissions().HasRight("club_regular"), Player.GetPermissions().HasRight("club_vip"), Player.GetPermissions().HasRight("super_admin")));
SendPacket(new AvatarEffectsComposer(Player.Effects().GetAllEffects));
SendPacket(new GetMinimailMessageCountComposer());
Subscription ActiveSubscription = null;
if (Player.GetSubscriptions().TryGetSubscription("habbo_club", out ActiveSubscription))
{
SendPacket(new ScrSendUserInfoComposer(ActiveSubscription, false));
}
else
{
SendPacket(new ScrSendUserInfoComposer(null, false));
}
Player.GetMessenger().SetUpdateNeeded(true);
if (Player.GetPermissions().HasRight("mod_tool"))
{
SendPacket(new ModeratorInitMessageComposer(Player.GetPermissions().HasRight("mod_tickets"),
Mango.GetServer().GetModerationManager().UserMessagePresets,
Mango.GetServer().GetModerationManager().RoomMessagePresets,
Mango.GetServer().GetModerationManager().UserActionPresets));
// todo: tickets sent
}
// Room Favourites
SendPacket(new FavouritesComposer(Player.Favourites().FavouriteRoomsId));
Player.InitProcess(); // always run this last please ty
if (!string.IsNullOrEmpty(MangoStaticGameSettings.LoginWelcomeMessage))
{
SendPacket(new HabboBroadcastComposer(MangoStaticGameSettings.LoginWelcomeMessage));
}
}
///
/// Sets the player for this session.
///
///
public void SetPlayer(Player Player)
{
if (this.Player != null)
{
throw new InvalidOperationException("Cannot set a player that has already been set."); // makes life easier when debugging...
}
this.Player = Player;
}
///
/// Retrieves the active player for this session.
///
///
public Player GetPlayer()
{
if (this.Player == null)
{
throw new NullReferenceException("Cannot access a player that has been disposed."); // makes life easier when debugging...
}
return this.Player;
}
}
}