aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVincent-Michael <Vincent_Michael@gmx.de>2014-06-23 23:47:48 +0200
committerVincent-Michael <Vincent_Michael@gmx.de>2014-06-23 23:47:48 +0200
commitd8234bed420728aaa2e0f329778730a906f7dd12 (patch)
treebe30b4b452967bed923c106971c35014ab4c9412 /src
parent9bac986f35067941552b7dec8da81eac127d2f66 (diff)
parentaa93a975469cca56e35adc1b5b501f4536be61f1 (diff)
Merge branch 'master' of github.com:TrinityCore/TrinityCore into 4.3.4
Conflicts: src/server/game/Accounts/AccountMgr.cpp src/server/game/Entities/Player/Player.h src/server/game/Server/WorldSocket.cpp src/server/game/World/World.cpp
Diffstat (limited to 'src')
-rw-r--r--src/server/authserver/Server/AuthSocket.cpp16
-rw-r--r--src/server/authserver/authserver.conf.dist7
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp2
-rw-r--r--src/server/game/Accounts/AccountMgr.cpp19
-rw-r--r--src/server/game/Entities/Player/Player.h8
-rw-r--r--src/server/game/Handlers/CalendarHandler.cpp39
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp17
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp4
-rw-r--r--src/server/game/Handlers/LootHandler.cpp3
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp4
-rw-r--r--src/server/game/Scripting/ScriptLoader.cpp12
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp48
-rw-r--r--src/server/game/Scripting/ScriptMgr.h45
-rw-r--r--src/server/game/Server/WorldSession.h5
-rw-r--r--src/server/game/Server/WorldSocket.cpp59
-rw-r--r--src/server/game/Spells/SpellEffects.cpp6
-rw-r--r--src/server/game/World/World.cpp4
-rw-r--r--src/server/game/World/World.h2
-rw-r--r--src/server/scripts/Commands/cs_account.cpp11
-rw-r--r--src/server/scripts/Custom/CMakeLists.txt3
-rw-r--r--src/server/scripts/Events/CMakeLists.txt4
-rw-r--r--src/server/scripts/World/CMakeLists.txt15
-rw-r--r--src/server/scripts/World/action_ip_logger.cpp315
-rw-r--r--src/server/shared/Database/Implementation/LoginDatabase.cpp12
-rw-r--r--src/server/shared/Database/Implementation/LoginDatabase.h7
-rw-r--r--src/server/worldserver/worldserver.conf.dist7
26 files changed, 607 insertions, 67 deletions
diff --git a/src/server/authserver/Server/AuthSocket.cpp b/src/server/authserver/Server/AuthSocket.cpp
index 7ca49b03b56..f1cb3af3f51 100644
--- a/src/server/authserver/Server/AuthSocket.cpp
+++ b/src/server/authserver/Server/AuthSocket.cpp
@@ -704,13 +704,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);
diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist
index 83edf9669ac..b7dee9ac08b 100644
--- a/src/server/authserver/authserver.conf.dist
+++ b/src/server/authserver/authserver.conf.dist
@@ -148,6 +148,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
+#
###################################################################################################
###################################################################################################
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index ecaa20284d7..e36433dd8c0 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -944,7 +944,7 @@ class SmartTrigger : public AreaTriggerScript
}
};
-void AddSC_SmartSCripts()
+void AddSC_SmartScripts()
{
new SmartTrigger();
}
diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp
index 59a8f046a6b..c7437909f05 100644
--- a/src/server/game/Accounts/AccountMgr.cpp
+++ b/src/server/game/Accounts/AccountMgr.cpp
@@ -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 AccountOpResult::AOR_NAME_NOT_EXIST; // account doesn't exist
+ }
if (utf8length(newPassword) > MAX_ACCOUNT_STR)
+ {
+ sScriptMgr->OnFailedPasswordChange(accountId);
return AccountOpResult::AOR_PASS_TOO_LONG;
+ }
Utf8ToUpperOnlyLatin(username);
Utf8ToUpperOnlyLatin(newPassword);
@@ -189,6 +196,7 @@ AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPass
LoginDatabase.Execute(stmt);
+ sScriptMgr->OnPasswordChange(accountId);
return AccountOpResult::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 AccountOpResult::AOR_NAME_NOT_EXIST; // account doesn't exist
+ }
if (utf8length(newEmail) > MAX_EMAIL_STR)
+ {
+ sScriptMgr->OnFailedEmailChange(accountId);
return AccountOpResult::AOR_EMAIL_TOO_LONG;
+ }
Utf8ToUpperOnlyLatin(username);
Utf8ToUpperOnlyLatin(newEmail);
@@ -212,6 +226,7 @@ AccountOpResult AccountMgr::ChangeEmail(uint32 accountId, std::string newEmail)
LoginDatabase.Execute(stmt);
+ sScriptMgr->OnEmailChange(accountId);
return AccountOpResult::AOR_OK;
}
@@ -223,7 +238,10 @@ AccountOpResult AccountMgr::ChangeRegEmail(uint32 accountId, std::string newEmai
return AccountOpResult::AOR_NAME_NOT_EXIST; // account doesn't exist
if (utf8length(newEmail) > MAX_EMAIL_STR)
+ {
+ sScriptMgr->OnFailedEmailChange(accountId);
return AccountOpResult::AOR_EMAIL_TOO_LONG;
+ }
Utf8ToUpperOnlyLatin(username);
Utf8ToUpperOnlyLatin(newEmail);
@@ -235,6 +253,7 @@ AccountOpResult AccountMgr::ChangeRegEmail(uint32 accountId, std::string newEmai
LoginDatabase.Execute(stmt);
+ sScriptMgr->OnEmailChange(accountId);
return AccountOpResult::AOR_OK;
}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 3d0300a681b..f60cf38cbc8 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -988,7 +988,7 @@ class PlayerTaxi
bool SetTaximaskNode(uint32 nodeidx)
{
uint8 field = uint8((nodeidx - 1) / 8);
- uint32 submask = 1 << ((nodeidx-1) % 8);
+ uint32 submask = 1 << ((nodeidx- 1) % 8);
if ((m_taximask[field] & submask) != submask)
{
m_taximask[field] |= submask;
@@ -1021,7 +1021,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;
@@ -2890,8 +2890,8 @@ class Player : public Unit, public GridObject<Player>
uint32 _maxPersonalArenaRate;
};
-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)
diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp
index 0461d7290d1..a7de4491a85 100644
--- a/src/server/game/Handlers/CalendarHandler.cpp
+++ b/src/server/game/Handlers/CalendarHandler.cpp
@@ -264,26 +264,43 @@ void WorldSession::HandleCalendarAddEvent(WorldPacket& recvData)
}
else
{
+ // client limits the amount of players to be invited to 100
+ const uint32 MaxPlayerInvites = 100;
+
uint32 inviteCount;
- recvData >> inviteCount;
+ uint64 invitee[MaxPlayerInvites];
+ uint8 status[MaxPlayerInvites];
+ uint8 rank[MaxPlayerInvites];
+
+ memset(invitee, 0, sizeof(invitee));
+ memset(status, 0, sizeof(status));
+ memset(rank, 0, sizeof(rank));
+
+ try
+ {
+ recvData >> inviteCount;
+
+ for (uint32 i = 0; i < inviteCount && i < MaxPlayerInvites; ++i)
+ {
+ recvData.readPackGUID(invitee[i]);
+ recvData >> status[i] >> rank[i];
+ }
+ }
+ catch (ByteBufferException const&)
+ {
+ delete calendarEvent;
+ calendarEvent = NULL;
+ throw;
+ }
SQLTransaction trans;
if (inviteCount > 1)
trans = CharacterDatabase.BeginTransaction();
- // client limits the amount of players to be invited to 100
- const uint32 MaxPlayerInvites = 100;
-
for (uint32 i = 0; i < inviteCount && i < MaxPlayerInvites; ++i)
{
- uint64 invitee = 0;
- uint8 status = 0;
- uint8 rank = 0;
- recvData.readPackGUID(invitee);
- recvData >> status >> rank;
-
// 946684800 is 01/01/2000 00:00:00 - default response time
- CalendarInvite* invite = new CalendarInvite(sCalendarMgr->GetFreeInviteId(), calendarEvent->GetEventId(), invitee, guid, 946684800, CalendarInviteStatus(status), CalendarModerationRank(rank), "");
+ CalendarInvite* invite = new CalendarInvite(sCalendarMgr->GetFreeInviteId(), calendarEvent->GetEventId(), invitee[i], guid, 946684800, CalendarInviteStatus(status[i]), CalendarModerationRank(rank[i]), "");
sCalendarMgr->AddInvite(calendarEvent, invite, trans);
}
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 86f978f1848..70547136bf8 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -721,10 +721,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;
@@ -733,6 +738,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);
@@ -742,6 +748,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);
@@ -760,12 +767,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
{
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index 741dec9bfec..d1d2988b255 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -1325,6 +1325,10 @@ void WorldSession::HandleItemRefund(WorldPacket &recvData)
return;
}
+ // Don't try to refund item currently being disenchanted
+ if (_player->GetLootGUID() == guid)
+ return;
+
GetPlayer()->RefundItem(item);
}
diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp
index b1132efd6a1..f2aee8f8de5 100644
--- a/src/server/game/Handlers/LootHandler.cpp
+++ b/src/server/game/Handlers/LootHandler.cpp
@@ -347,7 +347,8 @@ void WorldSession::DoLootRelease(uint64 lguid)
}
else
{
- if (pItem->loot.isLooted()) // Only delete item if no loot or money (unlooted loot is saved to db)
+ // Only delete item if no loot or money (unlooted loot is saved to db) or if it isn't an openable item
+ if (pItem->loot.isLooted() || !(proto->Flags & ITEM_PROTO_FLAG_OPENABLE))
player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
}
return; // item can be looted only single player
diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
index 3c9a43a4557..89cb5745267 100755
--- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp
@@ -191,6 +191,10 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di
}
else
{
+ // Set home position at place on waypoint movement.
+ if (!creature->GetTransGUID())
+ creature->SetHomePosition(creature->GetPosition());
+
if (creature->IsStopped())
Stop(STOP_TIME_FOR_PLAYER);
else if (creature->movespline->Finalized())
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
index d996a7b633e..6c77daa19d2 100644
--- a/src/server/game/Scripting/ScriptLoader.cpp
+++ b/src/server/game/Scripting/ScriptLoader.cpp
@@ -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();
@@ -98,6 +99,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
@@ -701,6 +703,7 @@ void AddSC_outdoorpvp_zm();
// player
void AddSC_chat_log();
+void AddSC_action_ip_logger();
#endif
@@ -708,7 +711,7 @@ void AddScripts()
{
AddExampleScripts();
AddSpellScripts();
- AddSC_SmartSCripts();
+ AddSC_SmartScripts();
AddCommandScripts();
#ifdef SCRIPTS
AddWorldScripts();
@@ -811,7 +814,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
}
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index 82189485ee6..ee0cb11983e 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -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
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index a0845b4c3d1..b8cea691ff8 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -29,6 +29,7 @@
#include "World.h"
#include "Weather.h"
+class AccountMgr;
class AuctionHouseObject;
class AuraScript;
class Battleground;
@@ -752,7 +753,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*/) { }
@@ -767,6 +771,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 /*accountI*/d) {}
+
+ // Called when an account login failed
+ virtual void OnFailedAccountLogin(uint32 /*accountI*/) {}
+
+ // Called when Email is successfully changed for Account
+ virtual void OnEmailChange(uint32 /*accountI*/) {}
+
+ // Called when Email failed to change for Account
+ virtual void OnFailedEmailChange(uint32 /*accountI*/) {}
+
+ // Called when Password is successfully changed for Account
+ virtual void OnPasswordChange(uint32 /*accountI*/) {}
+
+ // Called when Password failed to change for Account
+ virtual void OnFailedPasswordChange(uint32 /*accountI*/) {}
+};
+
class GuildScript : public ScriptObject
{
protected:
@@ -1036,11 +1067,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);
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 9c44fa8fad3..16ec751f137 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -1037,10 +1037,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;
diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp
index 58299a7194c..4e791cef767 100644
--- a/src/server/game/Server/WorldSocket.cpp
+++ b/src/server/game/Server/WorldSocket.cpp
@@ -276,7 +276,7 @@ int WorldSocket::open (void *a)
return 0;
}
-int WorldSocket::close (u_long)
+int WorldSocket::close(u_long)
{
shutdown();
@@ -287,7 +287,7 @@ int WorldSocket::close (u_long)
return 0;
}
-int WorldSocket::handle_input (ACE_HANDLE)
+int WorldSocket::handle_input(ACE_HANDLE)
{
if (closing_)
return -1;
@@ -323,7 +323,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);
@@ -369,7 +369,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);
@@ -430,7 +430,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
{
@@ -630,7 +630,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;
@@ -650,7 +650,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;
@@ -803,6 +803,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
std::string account;
SHA1Hash sha;
BigNumber k;
+ bool wardenActive = sWorld->getBoolConfig(CONFIG_WARDEN_ENABLED);
WorldPacket addonsData;
recvPacket.read_skip<uint32>();
@@ -877,6 +878,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;
@@ -889,19 +891,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();
@@ -926,10 +943,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;
}
@@ -953,7 +970,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);
@@ -961,6 +978,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;
}
@@ -971,6 +989,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;
}
@@ -985,8 +1004,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);
@@ -1009,8 +1026,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);
@@ -1028,8 +1044,11 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
m_Session->ReadAddonsInfo(addonsData);
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
@@ -1040,7 +1059,7 @@ int WorldSocket::HandleAuthSession(WorldPacket& recvPacket)
return 0;
}
-int WorldSocket::HandlePing (WorldPacket& recvPacket)
+int WorldSocket::HandlePing(WorldPacket& recvPacket)
{
uint32 ping;
uint32 latency;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index bdfe78d941a..708ff579182 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -700,10 +700,10 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex)
{
// remove all harmful spells on you...
SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo();
- if ((spell->DmgClass == SPELL_DAMAGE_CLASS_MAGIC // only affect magic spells
- || ((spell->GetDispelMask()) & dispelMask))
+ if (((spell->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && spell->GetSchoolMask() != SPELL_SCHOOL_MASK_NORMAL) // only affect magic spells
+ || (spell->GetDispelMask() & dispelMask)) &&
// ignore positive and passive auras
- && !iter->second->IsPositive() && !iter->second->GetBase()->IsPassive())
+ !iter->second->IsPositive() && !iter->second->GetBase()->IsPassive())
{
m_caster->RemoveAura(iter);
}
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 4375ad09e4c..d04b8b6015a 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1297,6 +1297,10 @@ void World::LoadConfigSettings(bool reload)
m_int_configs[CONFIG_PACKET_SPOOF_BANDURATION] = sConfigMgr->GetIntDefault("PacketSpoof.BanDuration", 86400);
+ 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);
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 7495939887a..9afb3c5e788 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -162,6 +162,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
};
diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp
index 3406c25eb16..c040f04b2bb 100644
--- a/src/server/scripts/Commands/cs_account.cpp
+++ b/src/server/scripts/Commands/cs_account.cpp
@@ -376,6 +376,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(),
@@ -387,6 +388,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(),
@@ -397,6 +399,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;
}
@@ -404,6 +407,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(),
@@ -417,6 +421,7 @@ public:
{
case AccountOpResult::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(),
@@ -424,6 +429,7 @@ public:
break;
case AccountOpResult::AOR_EMAIL_TOO_LONG:
handler->SendSysMessage(LANG_EMAIL_TOO_LONG);
+ sScriptMgr->OnFailedEmailChange(handler->GetSession()->GetAccountId());
handler->SetSentErrorMessage(true);
return false;
default:
@@ -468,6 +474,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(),
@@ -480,6 +487,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(),
@@ -492,6 +500,7 @@ public:
if (strcmp(newPassword, passwordConfirmation) != 0)
{
handler->SendSysMessage(LANG_NEW_PASSWORDS_NOT_MATCH);
+ sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId());
handler->SetSentErrorMessage(true);
return false;
}
@@ -502,12 +511,14 @@ public:
{
case AccountOpResult::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 AccountOpResult::AOR_PASS_TOO_LONG:
handler->SendSysMessage(LANG_PASSWORD_TOO_LONG);
+ sScriptMgr->OnFailedPasswordChange(handler->GetSession()->GetAccountId());
handler->SetSentErrorMessage(true);
return false;
default:
diff --git a/src/server/scripts/Custom/CMakeLists.txt b/src/server/scripts/Custom/CMakeLists.txt
index 78db719ae6e..80ebe36b555 100644
--- a/src/server/scripts/Custom/CMakeLists.txt
+++ b/src/server/scripts/Custom/CMakeLists.txt
@@ -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")
diff --git a/src/server/scripts/Events/CMakeLists.txt b/src/server/scripts/Events/CMakeLists.txt
index e45bc585007..3bdb6e6eac2 100644
--- a/src/server/scripts/Events/CMakeLists.txt
+++ b/src/server/scripts/Events/CMakeLists.txt
@@ -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")
diff --git a/src/server/scripts/World/CMakeLists.txt b/src/server/scripts/World/CMakeLists.txt
index 7d1b46732cf..56a0a1eb4c7 100644
--- a/src/server/scripts/World/CMakeLists.txt
+++ b/src/server/scripts/World/CMakeLists.txt
@@ -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")
diff --git a/src/server/scripts/World/action_ip_logger.cpp b/src/server/scripts/World/action_ip_logger.cpp
new file mode 100644
index 00000000000..73d92fb631a
--- /dev/null
+++ b/src/server/scripts/World/action_ip_logger.cpp
@@ -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, bool /*firstLogin*/)
+ {
+ 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();
+}
diff --git a/src/server/shared/Database/Implementation/LoginDatabase.cpp b/src/server/shared/Database/Implementation/LoginDatabase.cpp
index 5623bd56a95..8dc63fa48f2 100644
--- a/src/server/shared/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/shared/Database/Implementation/LoginDatabase.cpp
@@ -70,6 +70,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);
@@ -91,12 +92,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);
diff --git a/src/server/shared/Database/Implementation/LoginDatabase.h b/src/server/shared/Database/Implementation/LoginDatabase.h
index 3b4558874bc..83432009cbf 100644
--- a/src/server/shared/Database/Implementation/LoginDatabase.h
+++ b/src/server/shared/Database/Implementation/LoginDatabase.h
@@ -90,6 +90,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,
@@ -115,7 +116,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,
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 12d25d98b19..11a4bffee38 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -2694,6 +2694,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
+#
###################################################################################################
###################################################################################################