Merge pull request #11976 from Ascathor/master
Core/Misc: New ability to log account IP access history
This commit is contained in:
@@ -34,6 +34,7 @@ CREATE TABLE `account` (
|
||||
`reg_mail` varchar(255) NOT NULL DEFAULT '',
|
||||
`joindate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`last_ip` varchar(15) NOT NULL DEFAULT '127.0.0.1',
|
||||
`last_attempt_ip` varchar(15) NOT NULL DEFAULT '127.0.0.1',
|
||||
`failed_logins` int(10) unsigned NOT NULL DEFAULT '0',
|
||||
`locked` tinyint(3) unsigned NOT NULL DEFAULT '0',
|
||||
`lock_country` varchar(2) NOT NULL DEFAULT '00',
|
||||
@@ -239,6 +240,36 @@ LOCK TABLES `logs` WRITE;
|
||||
/*!40000 ALTER TABLE `logs` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `logs_ip_actions`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `logs_ip_actions`;
|
||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||
/*!40101 SET character_set_client = utf8 */;
|
||||
CREATE TABLE `logs_ip_actions` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique Identifier',
|
||||
`account_id` INT(10) UNSIGNED NOT NULL COMMENT 'Account ID',
|
||||
`character_guid` INT(10) UNSIGNED NOT NULL COMMENT 'Character Guid',
|
||||
`type` TINYINT(3) UNSIGNED NOT NULL,
|
||||
`ip` VARCHAR(15) NOT NULL DEFAULT '127.0.0.1',
|
||||
`systemnote` TEXT NULL COMMENT 'Notes inserted by system',
|
||||
`unixtime` INT(10) UNSIGNED NOT NULL COMMENT 'Unixtime',
|
||||
`time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Timestamp',
|
||||
`comment` TEXT NULL COMMENT 'Allows users to add a comment',
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8
|
||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||
|
||||
--
|
||||
-- Dumping data for table `logs_ip_actions`
|
||||
--
|
||||
|
||||
LOCK TABLES `logs_ip_actions` WRITE;
|
||||
/*!40000 ALTER TABLE `logs_ip_actions` DISABLE KEYS */;
|
||||
/*!40000 ALTER TABLE `logs_ip_actions` ENABLE KEYS */;
|
||||
UNLOCK TABLES;
|
||||
|
||||
--
|
||||
-- Table structure for table `rbac_account_permissions`
|
||||
--
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
ALTER TABLE `account`
|
||||
ADD COLUMN `last_attempt_ip` VARCHAR(15) NOT NULL DEFAULT '127.0.0.1' AFTER `last_ip`;
|
||||
|
||||
CREATE TABLE `logs_ip_actions` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique Identifier',
|
||||
`account_id` INT(10) UNSIGNED NOT NULL COMMENT 'Account ID',
|
||||
`character_guid` INT(10) UNSIGNED NOT NULL COMMENT 'Character Guid',
|
||||
`type` TINYINT(3) UNSIGNED NOT NULL,
|
||||
`ip` VARCHAR(15) NOT NULL DEFAULT '127.0.0.1',
|
||||
`systemnote` TEXT NULL COMMENT 'Notes inserted by system',
|
||||
`unixtime` INT(10) UNSIGNED NOT NULL COMMENT 'Unixtime',
|
||||
`time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Timestamp',
|
||||
`comment` TEXT NULL COMMENT 'Allows users to add a comment',
|
||||
PRIMARY KEY (`id`)
|
||||
)
|
||||
COMMENT='Used to log ips of individual actions'
|
||||
COLLATE='utf8_general_ci'
|
||||
ENGINE=InnoDB;
|
||||
@@ -715,13 +715,25 @@ bool AuthSocket::_HandleLogonProof()
|
||||
char data[4] = { AUTH_LOGON_PROOF, WOW_FAIL_UNKNOWN_ACCOUNT, 3, 0 };
|
||||
socket().send(data, sizeof(data));
|
||||
|
||||
TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str ());
|
||||
TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!", socket().getRemoteAddress().c_str(), socket().getRemotePort(), _login.c_str());
|
||||
|
||||
uint32 MaxWrongPassCount = sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0);
|
||||
|
||||
// We can not include the failed account login hook. However, this is a workaround to still log this.
|
||||
if (sConfigMgr->GetBoolDefault("Additional.IP.Based.Login.Logging", false))
|
||||
{
|
||||
PreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING);
|
||||
logstmt->setString(0, _login);
|
||||
logstmt->setString(1, socket().getRemoteAddress());
|
||||
logstmt->setString(2, "Logged on failed AccountLogin due wrong password");
|
||||
|
||||
LoginDatabase.Execute(logstmt);
|
||||
}
|
||||
|
||||
if (MaxWrongPassCount > 0)
|
||||
{
|
||||
//Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
|
||||
PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS);
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS);
|
||||
stmt->setString(0, _login);
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
|
||||
@@ -147,6 +147,13 @@ LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth"
|
||||
|
||||
LoginDatabase.WorkerThreads = 1
|
||||
|
||||
#
|
||||
# Wrong.Password.Login.Logging
|
||||
# Description: Additionally log attempted wrong password logging
|
||||
# Default: 0 - (Disabled)
|
||||
# 1 - (Enabled)
|
||||
|
||||
Wrong.Password.Login.Logging = 0
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
|
||||
@@ -944,7 +944,7 @@ class SmartTrigger : public AreaTriggerScript
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_SmartSCripts()
|
||||
void AddSC_SmartScripts()
|
||||
{
|
||||
new SmartTrigger();
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "DatabaseEnv.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Util.h"
|
||||
#include "SHA1.h"
|
||||
#include "WorldSession.h"
|
||||
@@ -166,10 +167,16 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPass
|
||||
std::string username;
|
||||
|
||||
if (!GetName(accountId, username))
|
||||
{
|
||||
sScriptMgr->OnFailedPasswordChange(accountId);
|
||||
return AOR_NAME_NOT_EXIST; // account doesn't exist
|
||||
}
|
||||
|
||||
if (utf8length(newPassword) > MAX_ACCOUNT_STR)
|
||||
{
|
||||
sScriptMgr->OnFailedPasswordChange(accountId);
|
||||
return AOR_PASS_TOO_LONG;
|
||||
}
|
||||
|
||||
normalizeString(username);
|
||||
normalizeString(newPassword);
|
||||
@@ -189,6 +196,7 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPass
|
||||
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
sScriptMgr->OnPasswordChange(accountId);
|
||||
return AOR_OK;
|
||||
}
|
||||
|
||||
@@ -197,10 +205,16 @@ AccountOpResult AccountMgr::ChangeEmail(uint32 accountId, std::string newEmail)
|
||||
std::string username;
|
||||
|
||||
if (!GetName(accountId, username))
|
||||
{
|
||||
sScriptMgr->OnFailedEmailChange(accountId);
|
||||
return AOR_NAME_NOT_EXIST; // account doesn't exist
|
||||
}
|
||||
|
||||
if (utf8length(newEmail) > MAX_EMAIL_STR)
|
||||
{
|
||||
sScriptMgr->OnFailedEmailChange(accountId);
|
||||
return AOR_EMAIL_TOO_LONG;
|
||||
}
|
||||
|
||||
normalizeString(username);
|
||||
normalizeString(newEmail);
|
||||
@@ -212,6 +226,7 @@ AccountOpResult AccountMgr::ChangeEmail(uint32 accountId, std::string newEmail)
|
||||
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
sScriptMgr->OnEmailChange(accountId);
|
||||
return AOR_OK;
|
||||
}
|
||||
|
||||
@@ -220,10 +235,16 @@ AccountOpResult AccountMgr::ChangeRegEmail(uint32 accountId, std::string newEmai
|
||||
std::string username;
|
||||
|
||||
if (!GetName(accountId, username))
|
||||
{
|
||||
sScriptMgr->OnFailedEmailChange(accountId);
|
||||
return AOR_NAME_NOT_EXIST; // account doesn't exist
|
||||
}
|
||||
|
||||
if (utf8length(newEmail) > MAX_EMAIL_STR)
|
||||
{
|
||||
sScriptMgr->OnFailedEmailChange(accountId);
|
||||
return AOR_EMAIL_TOO_LONG;
|
||||
}
|
||||
|
||||
normalizeString(username);
|
||||
normalizeString(newEmail);
|
||||
@@ -235,6 +256,7 @@ AccountOpResult AccountMgr::ChangeRegEmail(uint32 accountId, std::string newEmai
|
||||
|
||||
LoginDatabase.Execute(stmt);
|
||||
|
||||
sScriptMgr->OnEmailChange(accountId);
|
||||
return AOR_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -918,7 +918,7 @@ class PlayerTaxi
|
||||
bool SetTaximaskNode(uint32 nodeidx)
|
||||
{
|
||||
uint8 field = uint8((nodeidx - 1) / 32);
|
||||
uint32 submask = 1 << ((nodeidx-1) % 32);
|
||||
uint32 submask = 1 << ((nodeidx - 1) % 32);
|
||||
if ((m_taximask[field] & submask) != submask)
|
||||
{
|
||||
m_taximask[field] |= submask;
|
||||
@@ -951,7 +951,7 @@ class PlayerTaxi
|
||||
std::deque<uint32> m_TaxiDestinations;
|
||||
};
|
||||
|
||||
std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi);
|
||||
std::ostringstream& operator << (std::ostringstream& ss, PlayerTaxi const& taxi);
|
||||
|
||||
class Player;
|
||||
|
||||
@@ -2635,8 +2635,8 @@ class Player : public Unit, public GridObject<Player>
|
||||
uint32 _activeCheats;
|
||||
};
|
||||
|
||||
void AddItemsSetItem(Player*player, Item* item);
|
||||
void RemoveItemsSetItem(Player*player, ItemTemplate const* proto);
|
||||
void AddItemsSetItem(Player* player, Item* item);
|
||||
void RemoveItemsSetItem(Player* player, ItemTemplate const* proto);
|
||||
|
||||
// "the bodies of template functions must be made available in a header file"
|
||||
template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell* spell)
|
||||
|
||||
@@ -696,10 +696,15 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData)
|
||||
{
|
||||
uint64 guid;
|
||||
recvData >> guid;
|
||||
// Initiating
|
||||
uint32 initAccountId = GetAccountId();
|
||||
|
||||
// can't delete loaded character
|
||||
if (ObjectAccessor::FindPlayer(guid))
|
||||
{
|
||||
sScriptMgr->OnPlayerFailedDelete(guid, initAccountId);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 accountId = 0;
|
||||
uint8 level = 0;
|
||||
@@ -708,6 +713,7 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData)
|
||||
// is guild leader
|
||||
if (sGuildMgr->GetGuildByLeader(guid))
|
||||
{
|
||||
sScriptMgr->OnPlayerFailedDelete(guid, initAccountId);
|
||||
WorldPacket data(SMSG_CHAR_DELETE, 1);
|
||||
data << uint8(CHAR_DELETE_FAILED_GUILD_LEADER);
|
||||
SendPacket(&data);
|
||||
@@ -717,6 +723,7 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData)
|
||||
// is arena team captain
|
||||
if (sArenaTeamMgr->GetArenaTeamByCaptain(guid))
|
||||
{
|
||||
sScriptMgr->OnPlayerFailedDelete(guid, initAccountId);
|
||||
WorldPacket data(SMSG_CHAR_DELETE, 1);
|
||||
data << uint8(CHAR_DELETE_FAILED_ARENA_CAPTAIN);
|
||||
SendPacket(&data);
|
||||
@@ -735,12 +742,18 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData)
|
||||
}
|
||||
|
||||
// prevent deleting other players' characters using cheating tools
|
||||
if (accountId != GetAccountId())
|
||||
if (accountId != initAccountId)
|
||||
{
|
||||
sScriptMgr->OnPlayerFailedDelete(guid, initAccountId);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string IP_str = GetRemoteAddress();
|
||||
TC_LOG_INFO("entities.player.character", "Account: %d, IP: %s deleted character: %s, GUID: %u, Level: %u", accountId, IP_str.c_str(), name.c_str(), GUID_LOPART(guid), level);
|
||||
sScriptMgr->OnPlayerDelete(guid);
|
||||
|
||||
// To prevent hook failure, place hook before removing reference from DB
|
||||
sScriptMgr->OnPlayerDelete(guid, initAccountId); // To prevent race conditioning, but as it also makes sense, we hand the accountId over for successful delete.
|
||||
// Shouldn't interfere with character deletion though
|
||||
|
||||
if (sLog->ShouldLog("entities.player.dump", LOG_LEVEL_INFO)) // optimize GetPlayerDump call
|
||||
{
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "ScriptLoader.h"
|
||||
#include "World.h"
|
||||
|
||||
//examples
|
||||
void AddSC_example_creature();
|
||||
@@ -41,7 +42,7 @@ void AddSC_item_spell_scripts();
|
||||
void AddSC_example_spell_scripts();
|
||||
void AddSC_holiday_spell_scripts();
|
||||
|
||||
void AddSC_SmartSCripts();
|
||||
void AddSC_SmartScripts();
|
||||
|
||||
//Commands
|
||||
void AddSC_account_commandscript();
|
||||
@@ -97,6 +98,7 @@ void AddSC_npc_innkeeper();
|
||||
void AddSC_npcs_special();
|
||||
void AddSC_npc_taxi();
|
||||
void AddSC_achievement_scripts();
|
||||
void AddSC_action_ip_logger();
|
||||
|
||||
//eastern kingdoms
|
||||
void AddSC_alterac_valley(); //Alterac Valley
|
||||
@@ -695,6 +697,7 @@ void AddSC_outdoorpvp_zm();
|
||||
|
||||
// player
|
||||
void AddSC_chat_log();
|
||||
void AddSC_action_ip_logger();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -702,7 +705,7 @@ void AddScripts()
|
||||
{
|
||||
AddExampleScripts();
|
||||
AddSpellScripts();
|
||||
AddSC_SmartSCripts();
|
||||
AddSC_SmartScripts();
|
||||
AddCommandScripts();
|
||||
#ifdef SCRIPTS
|
||||
AddWorldScripts();
|
||||
@@ -803,7 +806,10 @@ void AddWorldScripts()
|
||||
AddSC_npcs_special();
|
||||
AddSC_npc_taxi();
|
||||
AddSC_achievement_scripts();
|
||||
AddSC_chat_log();
|
||||
AddSC_chat_log(); // location: scripts\World\chat_log.cpp
|
||||
// To avoid duplicate code, we check once /*ONLY*/ if logging is permitted or not.
|
||||
if (sWorld->getBoolConfig(CONFIG_IP_BASED_ACTION_LOGGING))
|
||||
AddSC_action_ip_logger(); // location: scripts\World\action_ip_logger.cpp
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -224,6 +224,7 @@ void ScriptMgr::Unload()
|
||||
SCR_CLEAR(TransportScript);
|
||||
SCR_CLEAR(AchievementCriteriaScript);
|
||||
SCR_CLEAR(PlayerScript);
|
||||
SCR_CLEAR(AccountScript);
|
||||
SCR_CLEAR(GuildScript);
|
||||
SCR_CLEAR(GroupScript);
|
||||
SCR_CLEAR(UnitScript);
|
||||
@@ -1257,9 +1258,14 @@ void ScriptMgr::OnPlayerCreate(Player* player)
|
||||
FOREACH_SCRIPT(PlayerScript)->OnCreate(player);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnPlayerDelete(uint64 guid)
|
||||
void ScriptMgr::OnPlayerDelete(uint64 guid, uint32 accountId)
|
||||
{
|
||||
FOREACH_SCRIPT(PlayerScript)->OnDelete(guid);
|
||||
FOREACH_SCRIPT(PlayerScript)->OnDelete(guid, accountId);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnPlayerFailedDelete(uint64 guid, uint32 accountId)
|
||||
{
|
||||
FOREACH_SCRIPT(PlayerScript)->OnFailedDelete(guid, accountId);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnPlayerSave(Player* player)
|
||||
@@ -1277,6 +1283,37 @@ void ScriptMgr::OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newAre
|
||||
FOREACH_SCRIPT(PlayerScript)->OnUpdateZone(player, newZone, newArea);
|
||||
}
|
||||
|
||||
// Account
|
||||
void ScriptMgr::OnAccountLogin(uint32 accountId)
|
||||
{
|
||||
FOREACH_SCRIPT(AccountScript)->OnAccountLogin(accountId);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnFailedAccountLogin(uint32 accountId)
|
||||
{
|
||||
FOREACH_SCRIPT(AccountScript)->OnFailedAccountLogin(accountId);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnEmailChange(uint32 accountId)
|
||||
{
|
||||
FOREACH_SCRIPT(AccountScript)->OnEmailChange(accountId);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnFailedEmailChange(uint32 accountId)
|
||||
{
|
||||
FOREACH_SCRIPT(AccountScript)->OnFailedEmailChange(accountId);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnPasswordChange(uint32 accountId)
|
||||
{
|
||||
FOREACH_SCRIPT(AccountScript)->OnPasswordChange(accountId);
|
||||
}
|
||||
|
||||
void ScriptMgr::OnFailedPasswordChange(uint32 accountId)
|
||||
{
|
||||
FOREACH_SCRIPT(AccountScript)->OnFailedPasswordChange(accountId);
|
||||
}
|
||||
|
||||
// Guild
|
||||
void ScriptMgr::OnGuildAddMember(Guild* guild, Player* player, uint8& plRank)
|
||||
{
|
||||
@@ -1539,6 +1576,12 @@ PlayerScript::PlayerScript(const char* name)
|
||||
ScriptRegistry<PlayerScript>::AddScript(this);
|
||||
}
|
||||
|
||||
AccountScript::AccountScript(const char* name)
|
||||
: ScriptObject(name)
|
||||
{
|
||||
ScriptRegistry<AccountScript>::AddScript(this);
|
||||
}
|
||||
|
||||
GuildScript::GuildScript(const char* name)
|
||||
: ScriptObject(name)
|
||||
{
|
||||
@@ -1581,6 +1624,7 @@ template class ScriptRegistry<PlayerScript>;
|
||||
template class ScriptRegistry<GuildScript>;
|
||||
template class ScriptRegistry<GroupScript>;
|
||||
template class ScriptRegistry<UnitScript>;
|
||||
template class ScriptRegistry<AccountScript>;
|
||||
|
||||
// Undefine utility macros.
|
||||
#undef GET_SCRIPT_RET
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "World.h"
|
||||
#include "Weather.h"
|
||||
|
||||
class AccountMgr;
|
||||
class AuctionHouseObject;
|
||||
class AuraScript;
|
||||
class Battleground;
|
||||
@@ -753,7 +754,10 @@ class PlayerScript : public UnitScript
|
||||
virtual void OnCreate(Player* /*player*/) { }
|
||||
|
||||
// Called when a player is deleted.
|
||||
virtual void OnDelete(uint64 /*guid*/) { }
|
||||
virtual void OnDelete(uint64 /*guid*/, uint32 /*accountId*/) { }
|
||||
|
||||
// Called when a player delete failed
|
||||
virtual void OnFailedDelete(uint64 /*guid*/, uint32 /*accountId*/) { }
|
||||
|
||||
// Called when a player is about to be saved.
|
||||
virtual void OnSave(Player* /*player*/) { }
|
||||
@@ -768,6 +772,33 @@ class PlayerScript : public UnitScript
|
||||
virtual void OnMapChanged(Player* /*player*/) { }
|
||||
};
|
||||
|
||||
class AccountScript : public ScriptObject
|
||||
{
|
||||
protected:
|
||||
|
||||
AccountScript(const char* name);
|
||||
|
||||
public:
|
||||
|
||||
// Called when an account logged in succesfully
|
||||
virtual void OnAccountLogin(uint32 accountId) {}
|
||||
|
||||
// Called when an account login failed
|
||||
virtual void OnFailedAccountLogin(uint32 accountId) {}
|
||||
|
||||
// Called when Email is successfully changed for Account
|
||||
virtual void OnEmailChange(uint32 accountId) {}
|
||||
|
||||
// Called when Email failed to change for Account
|
||||
virtual void OnFailedEmailChange(uint32 accountId) {}
|
||||
|
||||
// Called when Password is successfully changed for Account
|
||||
virtual void OnPasswordChange(uint32 accountId) {}
|
||||
|
||||
// Called when Password failed to change for Account
|
||||
virtual void OnFailedPasswordChange(uint32 accountId) {}
|
||||
};
|
||||
|
||||
class GuildScript : public ScriptObject
|
||||
{
|
||||
protected:
|
||||
@@ -1037,11 +1068,21 @@ class ScriptMgr
|
||||
void OnPlayerLogin(Player* player, bool firstLogin);
|
||||
void OnPlayerLogout(Player* player);
|
||||
void OnPlayerCreate(Player* player);
|
||||
void OnPlayerDelete(uint64 guid);
|
||||
void OnPlayerDelete(uint64 guid, uint32 accountId);
|
||||
void OnPlayerFailedDelete(uint64 guid, uint32 accountId);
|
||||
void OnPlayerSave(Player* player);
|
||||
void OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent);
|
||||
void OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newArea);
|
||||
|
||||
public: /* AccountScript */
|
||||
|
||||
void OnAccountLogin(uint32 accountId);
|
||||
void OnFailedAccountLogin(uint32 accountId);
|
||||
void OnEmailChange(uint32 accountId);
|
||||
void OnFailedEmailChange(uint32 accountId);
|
||||
void OnPasswordChange(uint32 accountId);
|
||||
void OnFailedPasswordChange(uint32 accountId);
|
||||
|
||||
public: /* GuildScript */
|
||||
|
||||
void OnGuildAddMember(Guild* guild, Player* player, uint8& plRank);
|
||||
|
||||
@@ -979,10 +979,11 @@ class WorldSession
|
||||
// characters who failed on Player::BuildEnumData shouldn't login
|
||||
std::set<uint32> _legitCharacters;
|
||||
|
||||
uint32 m_GUIDLow; // set loggined or recently logout player (while m_playerRecentlyLogout set)
|
||||
uint32 m_GUIDLow; // set logined or recently logout player (while m_playerRecentlyLogout set)
|
||||
Player* _player;
|
||||
WorldSocket* m_Socket;
|
||||
std::string m_Address;
|
||||
std::string m_Address; // Current Remote Address
|
||||
// std::string m_LAddress; // Last Attempted Remote Adress - we can not set attempted ip for a non-existing session!
|
||||
|
||||
AccountTypes _security;
|
||||
uint32 _accountId;
|
||||
|
||||
@@ -263,7 +263,7 @@ int WorldSocket::open (void *a)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WorldSocket::close (u_long)
|
||||
int WorldSocket::close(u_long)
|
||||
{
|
||||
shutdown();
|
||||
|
||||
@@ -274,7 +274,7 @@ int WorldSocket::close (u_long)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WorldSocket::handle_input (ACE_HANDLE)
|
||||
int WorldSocket::handle_input(ACE_HANDLE)
|
||||
{
|
||||
if (closing_)
|
||||
return -1;
|
||||
@@ -310,7 +310,7 @@ int WorldSocket::handle_input (ACE_HANDLE)
|
||||
ACE_NOTREACHED(return -1);
|
||||
}
|
||||
|
||||
int WorldSocket::handle_output (ACE_HANDLE)
|
||||
int WorldSocket::handle_output(ACE_HANDLE)
|
||||
{
|
||||
ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1);
|
||||
|
||||
@@ -356,7 +356,7 @@ int WorldSocket::handle_output (ACE_HANDLE)
|
||||
ACE_NOTREACHED (return 0);
|
||||
}
|
||||
|
||||
int WorldSocket::handle_output_queue (GuardType& g)
|
||||
int WorldSocket::handle_output_queue(GuardType& g)
|
||||
{
|
||||
if (msg_queue()->is_empty())
|
||||
return cancel_wakeup_output(g);
|
||||
@@ -417,7 +417,7 @@ int WorldSocket::handle_output_queue (GuardType& g)
|
||||
ACE_NOTREACHED(return -1);
|
||||
}
|
||||
|
||||
int WorldSocket::handle_close (ACE_HANDLE h, ACE_Reactor_Mask)
|
||||
int WorldSocket::handle_close(ACE_HANDLE h, ACE_Reactor_Mask)
|
||||
{
|
||||
// Critical section
|
||||
{
|
||||
@@ -617,7 +617,7 @@ int WorldSocket::handle_input_missing_data (void)
|
||||
return size_t(n) == recv_size ? 1 : 2;
|
||||
}
|
||||
|
||||
int WorldSocket::cancel_wakeup_output (GuardType& g)
|
||||
int WorldSocket::cancel_wakeup_output(GuardType& g)
|
||||
{
|
||||
if (!m_OutActive)
|
||||
return 0;
|
||||
@@ -637,7 +637,7 @@ int WorldSocket::cancel_wakeup_output (GuardType& g)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WorldSocket::schedule_wakeup_output (GuardType& g)
|
||||
int WorldSocket::schedule_wakeup_output(GuardType& g)
|
||||
{
|
||||
if (m_OutActive)
|
||||
return 0;
|
||||
@@ -758,6 +758,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
uint64 unk4;
|
||||
WorldPacket packet, SendAddonPacked;
|
||||
BigNumber k;
|
||||
bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED);
|
||||
|
||||
if (sWorld->IsClosed())
|
||||
{
|
||||
@@ -795,6 +796,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
// Stop if the account is not found
|
||||
if (!result)
|
||||
{
|
||||
// We can not log here, as we do not know the account. Thus, no accountId.
|
||||
SendAuthResponseError(AUTH_UNKNOWN_ACCOUNT);
|
||||
TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (unknown account).");
|
||||
return -1;
|
||||
@@ -807,19 +809,34 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
if (expansion > world_expansion)
|
||||
expansion = world_expansion;
|
||||
|
||||
// For hook purposes, we get Remoteaddress at this point.
|
||||
std::string address = GetRemoteAddress();
|
||||
|
||||
// As we don't know if attempted login process by ip works, we update last_attempt_ip right away
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_ATTEMPT_IP);
|
||||
|
||||
stmt->setString(0, address);
|
||||
stmt->setString(1, account);
|
||||
|
||||
LoginDatabase.Execute(stmt);
|
||||
// This also allows to check for possible "hack" attempts on account
|
||||
|
||||
// id has to be fetched at this point, so that first actual account response that fails can be logged
|
||||
id = fields[0].GetUInt32();
|
||||
|
||||
///- Re-check ip locking (same check as in realmd).
|
||||
if (fields[3].GetUInt8() == 1) // if ip is locked
|
||||
{
|
||||
if (strcmp (fields[2].GetCString(), GetRemoteAddress().c_str()))
|
||||
if (strcmp (fields[2].GetCString(), address.c_str()))
|
||||
{
|
||||
SendAuthResponseError(AUTH_FAILED);
|
||||
TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs).");
|
||||
TC_LOG_DEBUG("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs. Original IP: %s, new IP: %s).", fields[2].GetCString(), address.c_str());
|
||||
// We could log on hook only instead of an additional db log, however action logger is config based. Better keep DB logging as well
|
||||
sScriptMgr->OnFailedAccountLogin(id);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
id = fields[0].GetUInt32();
|
||||
|
||||
k.SetHexStr(fields[1].GetCString());
|
||||
|
||||
int64 mutetime = fields[5].GetInt64();
|
||||
@@ -844,10 +861,10 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
std::string os = fields[8].GetString();
|
||||
|
||||
// Must be done before WorldSession is created
|
||||
if (sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED) && os != "Win" && os != "OSX")
|
||||
if (wardenActive && os != "Win" && os != "OSX")
|
||||
{
|
||||
SendAuthResponseError(AUTH_REJECT);
|
||||
TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", GetRemoteAddress().c_str(), os.c_str());
|
||||
TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Client %s attempted to log in using invalid client OS (%s).", address.c_str(), os.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -871,7 +888,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BANS);
|
||||
|
||||
stmt->setUInt32(0, id);
|
||||
stmt->setString(1, GetRemoteAddress());
|
||||
stmt->setString(1, address);
|
||||
|
||||
PreparedQueryResult banresult = LoginDatabase.Query(stmt);
|
||||
|
||||
@@ -879,6 +896,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
{
|
||||
SendAuthResponseError(AUTH_BANNED);
|
||||
TC_LOG_ERROR("network", "WorldSocket::HandleAuthSession: Sent Auth Response (Account banned).");
|
||||
sScriptMgr->OnFailedAccountLogin(id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -889,6 +907,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
{
|
||||
SendAuthResponseError(AUTH_UNAVAILABLE);
|
||||
TC_LOG_INFO("network", "WorldSocket::HandleAuthSession: User tries to login but his security level is not enough");
|
||||
sScriptMgr->OnFailedAccountLogin(id);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -903,8 +922,6 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
sha.UpdateBigNumbers(&k, NULL);
|
||||
sha.Finalize();
|
||||
|
||||
std::string address = GetRemoteAddress();
|
||||
|
||||
if (memcmp(sha.GetDigest(), digest, 20))
|
||||
{
|
||||
SendAuthResponseError(AUTH_FAILED);
|
||||
@@ -927,8 +944,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
if (result)
|
||||
isRecruiter = true;
|
||||
|
||||
// Update the last_ip in the database
|
||||
|
||||
// Update the last_ip in the database as it was successful for login
|
||||
stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LAST_IP);
|
||||
|
||||
stmt->setString(0, address);
|
||||
@@ -946,8 +962,11 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
m_Session->ReadAddonsInfo(recvPacket);
|
||||
m_Session->LoadPermissions();
|
||||
|
||||
// At this point, we can safely hook a successful login
|
||||
sScriptMgr->OnAccountLogin(id);
|
||||
|
||||
// Initialize Warden system only if it is enabled by config
|
||||
if (sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED))
|
||||
if (wardenActive)
|
||||
m_Session->InitWarden(&k, os);
|
||||
|
||||
// Sleep this Network thread for
|
||||
@@ -958,7 +977,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WorldSocket::HandlePing (WorldPacket& recvPacket)
|
||||
int WorldSocket::HandlePing(WorldPacket& recvPacket)
|
||||
{
|
||||
uint32 ping;
|
||||
uint32 latency;
|
||||
|
||||
@@ -1253,6 +1253,10 @@ void World::LoadConfigSettings(bool reload)
|
||||
|
||||
m_int_configs[CONFIG_BIRTHDAY_TIME] = sConfigMgr->GetIntDefault("BirthdayTime", 1222964635);
|
||||
|
||||
m_bool_configs[CONFIG_IP_BASED_ACTION_LOGGING] = sConfigMgr->GetBoolDefault("Allow.IP.Based.Action.Logging", false);
|
||||
|
||||
m_bool_configs[CONFIG_IP_BASED_LOGIN_LOGGING] = sConfigMgr->GetBoolDefault("Wrong.Password.Login.Logging", false);
|
||||
|
||||
// call ScriptMgr if we're reloading the configuration
|
||||
if (reload)
|
||||
sScriptMgr->OnConfigLoad(reload);
|
||||
|
||||
@@ -154,6 +154,8 @@ enum WorldBoolConfigs
|
||||
CONFIG_EVENT_ANNOUNCE,
|
||||
CONFIG_STATS_LIMITS_ENABLE,
|
||||
CONFIG_INSTANCES_RESET_ANNOUNCE,
|
||||
CONFIG_IP_BASED_ACTION_LOGGING,
|
||||
CONFIG_IP_BASED_LOGIN_LOGGING,
|
||||
BOOL_CONFIG_VALUE_COUNT
|
||||
};
|
||||
|
||||
|
||||
@@ -377,6 +377,7 @@ public:
|
||||
if (!AccountMgr::CheckEmail(handler->GetSession()->GetAccountId(), std::string(oldEmail)))
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_WRONGEMAIL);
|
||||
sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId());
|
||||
handler->SetSentErrorMessage(true);
|
||||
TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Tried to change email, but the provided email [%s] is not equal to registration email [%s].",
|
||||
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(),
|
||||
@@ -388,6 +389,7 @@ public:
|
||||
if (!AccountMgr::CheckPassword(handler->GetSession()->GetAccountId(), std::string(password)))
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_WRONGOLDPASSWORD);
|
||||
sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId());
|
||||
handler->SetSentErrorMessage(true);
|
||||
TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Tried to change email, but the provided password is wrong.",
|
||||
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(),
|
||||
@@ -398,6 +400,7 @@ public:
|
||||
if (strcmp(email, oldEmail) == 0)
|
||||
{
|
||||
handler->SendSysMessage(LANG_OLD_EMAIL_IS_NEW_EMAIL);
|
||||
sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId());
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
@@ -405,6 +408,7 @@ public:
|
||||
if (strcmp(email, emailConfirmation) != 0)
|
||||
{
|
||||
handler->SendSysMessage(LANG_NEW_EMAILS_NOT_MATCH);
|
||||
sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId());
|
||||
handler->SetSentErrorMessage(true);
|
||||
TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Tried to change email, but the provided password is wrong.",
|
||||
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(),
|
||||
@@ -418,6 +422,7 @@ public:
|
||||
{
|
||||
case AOR_OK:
|
||||
handler->SendSysMessage(LANG_COMMAND_EMAIL);
|
||||
sScriptMgr->OnEmailChange(handler->GetSession()->GetAccountId());
|
||||
TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Changed Email from [%s] to [%s].",
|
||||
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(),
|
||||
handler->GetSession()->GetPlayer()->GetName().c_str(), handler->GetSession()->GetPlayer()->GetGUIDLow(),
|
||||
@@ -425,6 +430,7 @@ public:
|
||||
break;
|
||||
case AOR_EMAIL_TOO_LONG:
|
||||
handler->SendSysMessage(LANG_EMAIL_TOO_LONG);
|
||||
sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId());
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
default:
|
||||
@@ -469,6 +475,7 @@ public:
|
||||
if (!AccountMgr::CheckPassword(handler->GetSession()->GetAccountId(), std::string(oldPassword)))
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_WRONGOLDPASSWORD);
|
||||
sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId());
|
||||
handler->SetSentErrorMessage(true);
|
||||
TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Tried to change password, but the provided old password is wrong.",
|
||||
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(),
|
||||
@@ -481,6 +488,7 @@ public:
|
||||
&& !AccountMgr::CheckEmail(handler->GetSession()->GetAccountId(), std::string(emailConfirmation))) // ... and returns false if the comparison fails.
|
||||
{
|
||||
handler->SendSysMessage(LANG_COMMAND_WRONGEMAIL);
|
||||
sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId());
|
||||
handler->SetSentErrorMessage(true);
|
||||
TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Tried to change password, but the entered email [%s] is wrong.",
|
||||
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(),
|
||||
@@ -493,6 +501,7 @@ public:
|
||||
if (strcmp(newPassword, passwordConfirmation) != 0)
|
||||
{
|
||||
handler->SendSysMessage(LANG_NEW_PASSWORDS_NOT_MATCH);
|
||||
sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId());
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
@@ -503,12 +512,14 @@ public:
|
||||
{
|
||||
case AOR_OK:
|
||||
handler->SendSysMessage(LANG_COMMAND_PASSWORD);
|
||||
sScriptMgr->OnPasswordChange(handler->GetSession()->GetAccountId());
|
||||
TC_LOG_INFO("entities.player.character", "Account: %u (IP: %s) Character:[%s] (GUID: %u) Changed Password.",
|
||||
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress().c_str(),
|
||||
handler->GetSession()->GetPlayer()->GetName().c_str(), handler->GetSession()->GetPlayer()->GetGUIDLow());
|
||||
break;
|
||||
case AOR_PASS_TOO_LONG:
|
||||
handler->SendSysMessage(LANG_PASSWORD_TOO_LONG);
|
||||
sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId());
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
default:
|
||||
|
||||
@@ -8,8 +8,11 @@
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
# file(GLOB_RECURSE sources_Custom Custom/*.cpp Custom/*.h)
|
||||
|
||||
set(scripts_STAT_SRCS
|
||||
${scripts_STAT_SRCS}
|
||||
# ${sources_Custom}
|
||||
)
|
||||
|
||||
message(" -> Prepared: Custom")
|
||||
|
||||
@@ -8,9 +8,11 @@
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
file(GLOB_RECURSE sources_Events Events/*.cpp Events/*.h)
|
||||
|
||||
set(scripts_STAT_SRCS
|
||||
${scripts_STAT_SRCS}
|
||||
Events/childrens_week.cpp
|
||||
${sources_Events}
|
||||
)
|
||||
|
||||
message(" -> Prepared: Events")
|
||||
|
||||
@@ -8,20 +8,11 @@
|
||||
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
|
||||
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
file(GLOB_RECURSE sources_World World/*.cpp World/*.h)
|
||||
|
||||
set(scripts_STAT_SRCS
|
||||
${scripts_STAT_SRCS}
|
||||
World/achievement_scripts.cpp
|
||||
World/areatrigger_scripts.cpp
|
||||
World/boss_emerald_dragons.cpp
|
||||
World/chat_log.cpp
|
||||
World/go_scripts.cpp
|
||||
World/guards.cpp
|
||||
World/item_scripts.cpp
|
||||
World/mob_generic_creature.cpp
|
||||
World/npc_innkeeper.cpp
|
||||
World/npc_professions.cpp
|
||||
World/npc_taxi.cpp
|
||||
World/npcs_special.cpp
|
||||
${sources_World}
|
||||
)
|
||||
|
||||
message(" -> Prepared: World")
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ScriptMgr.h"
|
||||
#include "Channel.h"
|
||||
#include "Guild.h"
|
||||
#include "Group.h"
|
||||
|
||||
enum IPLoggingTypes
|
||||
{
|
||||
|
||||
// AccountActionIpLogger();
|
||||
ACCOUNT_LOGIN = 0,
|
||||
ACCOUNT_FAIL_LOGIN = 1,
|
||||
ACCOUNT_CHANGE_PW = 2,
|
||||
ACCOUNT_CHANGE_PW_FAIL = 3, // Only two types of account changes exist...
|
||||
ACCOUNT_CHANGE_EMAIL = 4,
|
||||
ACCOUNT_CHANGE_EMAIL_FAIL = 5, // ...so we log them individually
|
||||
// OBSOLETE - ACCOUNT_LOGOUT = 6, /* Can not be logged. We still keep the type however */
|
||||
// CharacterActionIpLogger();
|
||||
CHARACTER_CREATE = 7,
|
||||
CHARACTER_LOGIN = 8,
|
||||
CHARACTER_LOGOUT = 9,
|
||||
// CharacterDeleteActionIpLogger();
|
||||
CHARACTER_DELETE = 10,
|
||||
CHARACTER_FAILED_DELETE = 11,
|
||||
// AccountActionIpLogger(), CharacterActionIpLogger(), CharacterActionIpLogger();
|
||||
UNKNOWN_ACTION = 12
|
||||
};
|
||||
|
||||
class AccountActionIpLogger : public AccountScript
|
||||
{
|
||||
public:
|
||||
AccountActionIpLogger() : AccountScript("AccountActionIpLogger") { }
|
||||
|
||||
// We log last_ip instead of last_attempt_ip, as login was successful
|
||||
// ACCOUNT_LOGIN = 0
|
||||
void OnAccountLogin(uint32 accountId)
|
||||
{
|
||||
AccountIPLogAction(accountId, ACCOUNT_LOGIN);
|
||||
}
|
||||
|
||||
// We log last_attempt_ip instead of last_ip, as failed login doesn't necessarily mean approperiate user
|
||||
// ACCOUNT_FAIL_LOGIN = 1
|
||||
void OnFailedAccountLogin(uint32 accountId)
|
||||
{
|
||||
AccountIPLogAction(accountId, ACCOUNT_FAIL_LOGIN);
|
||||
}
|
||||
|
||||
// ACCOUNT_CHANGE_PW = 2
|
||||
void OnPasswordChange(uint32 accountId)
|
||||
{
|
||||
AccountIPLogAction(accountId, ACCOUNT_CHANGE_PW);
|
||||
}
|
||||
|
||||
// ACCOUNT_CHANGE_PW_FAIL = 3
|
||||
void OnFailedPasswordChange(uint32 accountId)
|
||||
{
|
||||
AccountIPLogAction(accountId, ACCOUNT_CHANGE_PW_FAIL);
|
||||
}
|
||||
|
||||
// Registration Email can NOT be changed apart from GM level users. Thus, we do not require to log them...
|
||||
// ACCOUNT_CHANGE_EMAIL = 4
|
||||
void OnEmailChange(uint32 accountId)
|
||||
{
|
||||
AccountIPLogAction(accountId, ACCOUNT_CHANGE_EMAIL); // ... they get logged by gm command logger anyway
|
||||
}
|
||||
|
||||
// ACCOUNT_CHANGE_EMAIL_FAIL = 5
|
||||
void OnFailedEmailChange(uint32 accountId)
|
||||
{
|
||||
AccountIPLogAction(accountId, ACCOUNT_CHANGE_EMAIL_FAIL);
|
||||
}
|
||||
|
||||
/* It's impossible to log the account logout process out of character selection - shouldn't matter anyway,
|
||||
* as ip doesn't change through playing (obviously).*/
|
||||
// ACCOUNT_LOGOUT = 6
|
||||
void AccountIPLogAction(uint32 accountId, IPLoggingTypes aType)
|
||||
{
|
||||
// Action IP Logger is only intialized if config is set up
|
||||
// Else, this script isn't loaded in the first place: We require no config check.
|
||||
|
||||
// We declare all the required variables
|
||||
uint32 playerGuid = accountId;
|
||||
uint32 characterGuid = 0;
|
||||
std::string systemNote = "ERROR"; // "ERROR" is a placeholder here. We change it later.
|
||||
|
||||
// With this switch, we change systemNote so that we have a more accurate phrasing of what type it is.
|
||||
// Avoids Magicnumbers in SQL table
|
||||
switch (aType)
|
||||
{
|
||||
case ACCOUNT_LOGIN:
|
||||
systemNote = "Logged on Successful AccountLogin";
|
||||
break;
|
||||
case ACCOUNT_FAIL_LOGIN:
|
||||
systemNote = "Logged on Failed AccountLogin";
|
||||
break;
|
||||
case ACCOUNT_CHANGE_PW:
|
||||
systemNote = "Logged on Successful Account Password Change";
|
||||
break;
|
||||
case ACCOUNT_CHANGE_PW_FAIL:
|
||||
systemNote = "Logged on Failed Account Password Change";
|
||||
break;
|
||||
case ACCOUNT_CHANGE_EMAIL:
|
||||
systemNote = "Logged on Successful Account Email Change";
|
||||
break;
|
||||
case ACCOUNT_CHANGE_EMAIL_FAIL:
|
||||
systemNote = "Logged on Failed Account Email Change";
|
||||
break;
|
||||
/*case ACCOUNT_LOGOUT:
|
||||
systemNote = "Logged on AccountLogout"; //Can not be logged
|
||||
break;*/
|
||||
// Neither should happen. Ever. Period. If it does, call Ghostbusters and all your local software defences to investigate.
|
||||
case UNKNOWN_ACTION:
|
||||
default:
|
||||
systemNote = "ERROR! Unknown action!";
|
||||
break;
|
||||
}
|
||||
|
||||
// Once we have done everything, we can insert the new log.
|
||||
// Seeing as the time differences should be minimal, we do not get unixtime and the timestamp right now;
|
||||
// Rather, we let it be added with the SQL query.
|
||||
if (aType != ACCOUNT_FAIL_LOGIN)
|
||||
{
|
||||
// As we can assume most account actions are NOT failed login, so this is the more accurate check.
|
||||
// For those, we need last_ip...
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ALDL_IP_LOGGING);
|
||||
|
||||
stmt->setUInt32(0, playerGuid);
|
||||
stmt->setUInt32(1, characterGuid);
|
||||
stmt->setUInt8(2, aType);
|
||||
stmt->setUInt32(3, playerGuid);
|
||||
stmt->setString(4, systemNote.c_str());
|
||||
LoginDatabase.Execute(stmt);
|
||||
}
|
||||
else // ... but for failed login, we query last_attempt_ip from account table. Which we do with an unique query
|
||||
{
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FACL_IP_LOGGING);
|
||||
|
||||
stmt->setUInt32(0, playerGuid);
|
||||
stmt->setUInt32(1, characterGuid);
|
||||
stmt->setUInt8(2, aType);
|
||||
stmt->setUInt32(3, playerGuid);
|
||||
stmt->setString(4, systemNote.c_str());
|
||||
LoginDatabase.Execute(stmt);
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
class CharacterActionIpLogger : public PlayerScript
|
||||
{
|
||||
public:
|
||||
CharacterActionIpLogger() : PlayerScript("CharacterActionIpLogger") { }
|
||||
|
||||
// CHARACTER_CREATE = 7
|
||||
void OnCreate(Player* player)
|
||||
{
|
||||
CharacterIPLogAction(player, CHARACTER_CREATE);
|
||||
}
|
||||
|
||||
// CHARACTER_LOGIN = 8
|
||||
void OnLogin(Player* player)
|
||||
{
|
||||
CharacterIPLogAction(player, CHARACTER_LOGIN);
|
||||
}
|
||||
|
||||
// CHARACTER_LOGOUT = 9
|
||||
void OnLogout(Player* player)
|
||||
{
|
||||
CharacterIPLogAction(player, CHARACTER_LOGOUT);
|
||||
}
|
||||
|
||||
// CHARACTER_DELETE = 10
|
||||
// CHARACTER_FAILED_DELETE = 11
|
||||
// We don't log either here - they require a guid
|
||||
|
||||
// UNKNOWN_ACTION = 12
|
||||
// There is no real hook we could use for that.
|
||||
// Shouldn't happen anyway, should it ? Nothing to see here.
|
||||
|
||||
/// Logs a number of actions done by players with an IP
|
||||
void CharacterIPLogAction(Player* player, IPLoggingTypes aType)
|
||||
{
|
||||
// Action IP Logger is only intialized if config is set up
|
||||
// Else, this script isn't loaded in the first place: We require no config check.
|
||||
|
||||
// We declare all the required variables
|
||||
uint32 playerGuid = player->GetSession()->GetAccountId();
|
||||
uint32 characterGuid = player->GetGUIDLow();
|
||||
const std::string currentIp = player->GetSession()->GetRemoteAddress();
|
||||
std::string systemNote = "ERROR"; // "ERROR" is a placeholder here. We change it...
|
||||
|
||||
// ... with this switch, so that we have a more accurate phrasing of what type it is
|
||||
switch (aType)
|
||||
{
|
||||
case CHARACTER_CREATE:
|
||||
systemNote = "Logged on CharacterCreate";
|
||||
break;
|
||||
case CHARACTER_LOGIN:
|
||||
systemNote = "Logged on CharacterLogin";
|
||||
break;
|
||||
case CHARACTER_LOGOUT:
|
||||
systemNote = "Logged on CharacterLogout";
|
||||
break;
|
||||
case CHARACTER_DELETE:
|
||||
systemNote = "Logged on CharacterDelete";
|
||||
break;
|
||||
case CHARACTER_FAILED_DELETE:
|
||||
systemNote = "Logged on Failed CharacterDelete";
|
||||
break;
|
||||
// Neither should happen. Ever. Period. If it does, call Mythbusters.
|
||||
case UNKNOWN_ACTION:
|
||||
default:
|
||||
systemNote = "ERROR! Unknown action!";
|
||||
break;
|
||||
}
|
||||
|
||||
// Once we have done everything, we can insert the new log.
|
||||
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_CHAR_IP_LOGGING);
|
||||
|
||||
stmt->setUInt32(0, playerGuid);
|
||||
stmt->setUInt32(1, characterGuid);
|
||||
stmt->setUInt8(2, aType);
|
||||
stmt->setString(3, currentIp.c_str()); // We query the ip here.
|
||||
stmt->setString(4, systemNote.c_str());
|
||||
// Seeing as the time differences should be minimal, we do not get unixtime and the timestamp right now;
|
||||
// Rather, we let it be added with the SQL query.
|
||||
|
||||
LoginDatabase.Execute(stmt);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
class CharacterDeleteActionIpLogger : public PlayerScript
|
||||
{
|
||||
public:
|
||||
CharacterDeleteActionIpLogger() : PlayerScript("CharacterDeleteActionIpLogger") { }
|
||||
|
||||
// CHARACTER_DELETE = 10
|
||||
void OnDelete(uint64 guid, uint32 accountId)
|
||||
{
|
||||
DeleteIPLogAction(guid, accountId, CHARACTER_DELETE);
|
||||
}
|
||||
|
||||
// CHARACTER_FAILED_DELETE = 11
|
||||
void OnFailedDelete(uint64 guid, uint32 accountId)
|
||||
{
|
||||
DeleteIPLogAction(guid, accountId, CHARACTER_FAILED_DELETE);
|
||||
}
|
||||
|
||||
void DeleteIPLogAction(uint64 guid, uint32 playerGuid, IPLoggingTypes aType)
|
||||
{
|
||||
// Action IP Logger is only intialized if config is set up
|
||||
// Else, this script isn't loaded in the first place: We require no config check.
|
||||
|
||||
// We declare all the required variables
|
||||
uint32 characterGuid = GUID_LOPART(guid); // We have no access to any member function of Player* or WorldSession*. So use old-fashioned way.
|
||||
// Query playerGuid/accountId, as we only have characterGuid
|
||||
std::string systemNote = "ERROR"; // "ERROR" is a placeholder here. We change it later.
|
||||
|
||||
// With this switch, we change systemNote so that we have a more accurate phrasing of what type it is.
|
||||
// Avoids Magicnumbers in SQL table
|
||||
switch (aType)
|
||||
{
|
||||
case CHARACTER_DELETE:
|
||||
systemNote = "Logged on CharacterDelete";
|
||||
break;
|
||||
case CHARACTER_FAILED_DELETE:
|
||||
systemNote = "Logged on Failed CharacterDelete";
|
||||
break;
|
||||
// Neither should happen. Ever. Period. If it does, call to whatever god you have for mercy and guidance.
|
||||
case UNKNOWN_ACTION:
|
||||
default:
|
||||
systemNote = "ERROR! Unknown action!";
|
||||
break;
|
||||
}
|
||||
|
||||
// Once we have done everything, we can insert the new log.
|
||||
PreparedStatement* stmt2 = LoginDatabase.GetPreparedStatement(LOGIN_INS_ALDL_IP_LOGGING);
|
||||
|
||||
stmt2->setUInt32(0, playerGuid);
|
||||
stmt2->setUInt32(1, characterGuid);
|
||||
stmt2->setUInt8(2, aType);
|
||||
stmt2->setUInt32(3, playerGuid);
|
||||
stmt2->setString(4, systemNote.c_str());
|
||||
// Seeing as the time differences should be minimal, we do not get unixtime and the timestamp right now;
|
||||
// Rather, we let it be added with the SQL query.
|
||||
|
||||
LoginDatabase.Execute(stmt2);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void AddSC_action_ip_logger()
|
||||
{
|
||||
new AccountActionIpLogger();
|
||||
new CharacterActionIpLogger();
|
||||
new CharacterDeleteActionIpLogger();
|
||||
}
|
||||
@@ -69,6 +69,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(LOGIN_UPD_MUTE_TIME, "UPDATE account SET mutetime = ? , mutereason = ? , muteby = ? WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_MUTE_TIME_LOGIN, "UPDATE account SET mutetime = ? WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_LAST_IP, "UPDATE account SET last_ip = ? WHERE username = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_LAST_ATTEMPT_IP, "UPDATE account SET last_attempt_ip = ? WHERE username = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_ACCOUNT_ONLINE, "UPDATE account SET online = 1 WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_UPTIME_PLAYERS, "UPDATE uptime SET uptime = ?, maxplayers = ? WHERE realmid = ? AND starttime = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_DEL_OLD_LOGS, "DELETE FROM logs WHERE (time + ?) < ?", CONNECTION_ASYNC);
|
||||
@@ -90,12 +91,21 @@ void LoginDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_RECRUITER, "SELECT 1 FROM account WHERE recruiter = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_BANS, "SELECT 1 FROM account_banned WHERE id = ? AND active = 1 UNION SELECT 1 FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_WHOIS, "SELECT username, email, last_ip FROM account WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_LAST_ATTEMPT_IP, "SELECT last_attempt_ip FROM account WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_LAST_IP, "SELECT last_ip FROM account WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL, "SELECT allowedSecurityLevel from realmlist WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_DEL_ACCOUNT, "DELETE FROM account WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_IP2NATION_COUNTRY, "SELECT c.country FROM ip2nationCountries c, ip2nation i WHERE i.ip < ? AND c.code = i.country ORDER BY i.ip DESC LIMIT 0,1", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_GET_EMAIL_BY_ID, "SELECT email FROM account WHERE id = ?", CONNECTION_SYNCH);
|
||||
|
||||
// 0: uint32, 1: uint32, 2: uint8, 3: uint32, 4: string // Complete name: "Login_Insert_AccountLoginDeLete_IP_Logging"
|
||||
PrepareStatement(LOGIN_INS_ALDL_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, (SELECT last_ip FROM account WHERE id = ?), ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC);
|
||||
// 0: uint32, 1: uint32, 2: uint8, 3: uint32, 4: string // Complete name: "Login_Insert_FailedAccountLogin_IP_Logging"
|
||||
PrepareStatement(LOGIN_INS_FACL_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, (SELECT last_attempt_ip FROM account WHERE id = ?), ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC);
|
||||
// 0: uint32, 1: uint32, 2: uint8, 3: string, 4: string // Complete name: "Login_Insert_CharacterDelete_IP_Logging"
|
||||
PrepareStatement(LOGIN_INS_CHAR_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC);
|
||||
// 0: string, 1: string, 2: string // Complete name: "Login_Insert_Failed_Account_Login_due_password_IP_Logging"
|
||||
PrepareStatement(LOGIN_INS_FALP_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES ((SELECT id FROM account WHERE username = ?), 0, 1, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS_BY_ID, "SELECT gmlevel, RealmID FROM account_access WHERE id = ? and (RealmID = ? OR RealmID = -1) ORDER BY gmlevel desc", CONNECTION_SYNCH);
|
||||
|
||||
PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId", CONNECTION_SYNCH);
|
||||
|
||||
@@ -89,6 +89,7 @@ enum LoginDatabaseStatements
|
||||
LOGIN_UPD_MUTE_TIME,
|
||||
LOGIN_UPD_MUTE_TIME_LOGIN,
|
||||
LOGIN_UPD_LAST_IP,
|
||||
LOGIN_UPD_LAST_ATTEMPT_IP,
|
||||
LOGIN_UPD_ACCOUNT_ONLINE,
|
||||
LOGIN_UPD_UPTIME_PLAYERS,
|
||||
LOGIN_DEL_OLD_LOGS,
|
||||
@@ -114,7 +115,13 @@ enum LoginDatabaseStatements
|
||||
LOGIN_DEL_ACCOUNT,
|
||||
LOGIN_SEL_IP2NATION_COUNTRY,
|
||||
LOGIN_SEL_AUTOBROADCAST,
|
||||
LOGIN_SEL_LAST_ATTEMPT_IP,
|
||||
LOGIN_SEL_LAST_IP,
|
||||
LOGIN_GET_EMAIL_BY_ID,
|
||||
LOGIN_INS_ALDL_IP_LOGGING,
|
||||
LOGIN_INS_FACL_IP_LOGGING,
|
||||
LOGIN_INS_CHAR_IP_LOGGING,
|
||||
LOGIN_INS_FALP_IP_LOGGING,
|
||||
|
||||
LOGIN_SEL_ACCOUNT_ACCESS_BY_ID,
|
||||
LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS,
|
||||
|
||||
@@ -2734,6 +2734,13 @@ Logger.sql.sql=5,Console DBErrors
|
||||
|
||||
Log.Async.Enable = 0
|
||||
|
||||
#
|
||||
# Allow.IP.Based.Action.Logging
|
||||
# Description: Logs actions, e.g. account login and logout to name a few, based on IP of current session.
|
||||
# Default: 0 - (Disabled)
|
||||
# 1 - (Enabled)
|
||||
|
||||
Allow.IP.Based.Action.Logging = 0
|
||||
#
|
||||
###################################################################################################
|
||||
|
||||
|
||||
Reference in New Issue
Block a user