Skip to content

Commit

Permalink
Core/Players: Implemented serverside validation of reserved/profane n…
Browse files Browse the repository at this point in the history
…ames

Closes #15357

New library dependency: Boost.Regex
  • Loading branch information
Shauren committed Aug 29, 2015
1 parent b010780 commit b564c10
Show file tree
Hide file tree
Showing 18 changed files with 226 additions and 36 deletions.
2 changes: 1 addition & 1 deletion cmake/macros/ConfigureBoost.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ if(WIN32)
add_definitions(-D_WIN32_WINNT=${ver})
endif()

find_package(Boost 1.49 REQUIRED system filesystem thread program_options iostreams)
find_package(Boost 1.49 REQUIRED system filesystem thread program_options iostreams regex)
add_definitions(-DBOOST_DATE_TIME_NO_LIB)
add_definitions(-DBOOST_REGEX_NO_LIB)
add_definitions(-DBOOST_CHRONO_NO_LIB)
Expand Down
37 changes: 37 additions & 0 deletions sql/updates/hotfixes/2015_08_30_00_hotfixes.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--
-- Table structure for table `names_profanity`
--

DROP TABLE IF EXISTS `names_profanity`;
CREATE TABLE `names_profanity` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`Name` text,
`Language` int(10) NOT NULL DEFAULT '0',
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

--
-- Table structure for table `names_reserved`
--

DROP TABLE IF EXISTS `names_reserved`;
CREATE TABLE `names_reserved` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`Name` text,
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

--
-- Table structure for table `names_reserved_locale`
--

DROP TABLE IF EXISTS `names_reserved_locale`;
CREATE TABLE `names_reserved_locale` (
`ID` int(10) unsigned NOT NULL DEFAULT '0',
`Name` text,
`LocaleMask` int(10) unsigned NOT NULL DEFAULT '0',
`VerifiedBuild` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
1 change: 1 addition & 0 deletions src/common/Define.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ enum DBCFormer
FT_NA = 'x', //not used or unknown, 4 byte size
FT_NA_BYTE = 'X', //not used or unknown, byte
FT_STRING = 's', //char*
FT_STRING_NOT_LOCALIZED = 'S', //char* but without locale in DB2
FT_FLOAT = 'f', //float
FT_INT = 'i', //uint32
FT_BYTE = 'b', //uint8
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,15 @@ void HotfixDatabaseConnection::DoPrepareStatements()
PrepareStatement(HOTFIX_SEL_NAME_GEN, "SELECT ID, Name, Race, Sex FROM name_gen ORDER BY ID DESC", CONNECTION_SYNCH);
PREPARE_LOCALE_STMT(HOTFIX_SEL_NAME_GEN, "SELECT ID, Name_lang FROM name_gen_locale WHERE locale = ?", CONNECTION_SYNCH);

// NamesProfanity.db2
PrepareStatement(HOTFIX_SEL_NAMES_PROFANITY, "SELECT ID, Name, Language FROM names_profanity ORDER BY ID DESC", CONNECTION_SYNCH);

// NamesReserved.db2
PrepareStatement(HOTFIX_SEL_NAMES_RESERVED, "SELECT ID, Name FROM names_reserved ORDER BY ID DESC", CONNECTION_SYNCH);

// NamesReservedLocale.db2
PrepareStatement(HOTFIX_SEL_NAMES_RESERVED_LOCALE, "SELECT ID, Name, LocaleMask FROM names_reserved_locale ORDER BY ID DESC", CONNECTION_SYNCH);

// OverrideSpellData.db2
PrepareStatement(HOTFIX_SEL_OVERRIDE_SPELL_DATA, "SELECT ID, SpellID1, SpellID2, SpellID3, SpellID4, SpellID5, SpellID6, SpellID7, SpellID8, "
"SpellID9, SpellID10, Flags, PlayerActionbarFileDataID FROM override_spell_data ORDER BY ID DESC", CONNECTION_SYNCH);
Expand Down
6 changes: 6 additions & 0 deletions src/server/database/Database/Implementation/HotfixDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,12 @@ enum HotfixDatabaseStatements
HOTFIX_SEL_NAME_GEN,
HOTFIX_SEL_NAME_GEN_LOCALE,

HOTFIX_SEL_NAMES_PROFANITY,

HOTFIX_SEL_NAMES_RESERVED,

HOTFIX_SEL_NAMES_RESERVED_LOCALE,

HOTFIX_SEL_OVERRIDE_SPELL_DATA,

HOTFIX_SEL_PHASE_X_PHASE_GROUP,
Expand Down
41 changes: 41 additions & 0 deletions src/server/game/DataStores/DB2Stores.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ DB2Storage<MountCapabilityEntry> sMountCapabilityStore("MountCapa
DB2Storage<MountEntry> sMountStore("Mount.db2", MountFormat, HOTFIX_SEL_MOUNT);
DB2Storage<MountTypeXCapabilityEntry> sMountTypeXCapabilityStore("MountTypeXCapability.db2", MountTypeXCapabilityFormat, HOTFIX_SEL_MOUNT_TYPE_X_CAPABILITY);
DB2Storage<NameGenEntry> sNameGenStore("NameGen.db2", NameGenFormat, HOTFIX_SEL_NAME_GEN);
DB2Storage<NamesProfanityEntry> sNamesProfanityStore("NamesProfanity.db2", NamesProfanityFormat, HOTFIX_SEL_NAMES_PROFANITY);
DB2Storage<NamesReservedEntry> sNamesReservedStore("NamesReserved.db2", NamesReservedFormat, HOTFIX_SEL_NAMES_RESERVED);
DB2Storage<NamesReservedLocaleEntry> sNamesReservedLocaleStore("NamesReservedLocale.db2", NamesReservedLocaleFormat, HOTFIX_SEL_NAMES_RESERVED_LOCALE);
DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore("OverrideSpellData.db2", OverrideSpellDataFormat, HOTFIX_SEL_OVERRIDE_SPELL_DATA);
DB2Storage<PhaseXPhaseGroupEntry> sPhaseXPhaseGroupStore("PhaseXPhaseGroup.db2", PhaseXPhaseGroupFormat, HOTFIX_SEL_PHASE_X_PHASE_GROUP);
DB2Storage<QuestMoneyRewardEntry> sQuestMoneyRewardStore("QuestMoneyReward.db2", QuestMoneyRewardFormat, HOTFIX_SEL_QUEST_MONEY_REWARD);
Expand Down Expand Up @@ -244,6 +247,9 @@ void DB2Manager::LoadStores(std::string const& dataPath)
LOAD_DB2(sMountStore);
LOAD_DB2(sMountTypeXCapabilityStore);
LOAD_DB2(sNameGenStore);
LOAD_DB2(sNamesProfanityStore);
LOAD_DB2(sNamesReservedStore);
LOAD_DB2(sNamesReservedLocaleStore);
LOAD_DB2(sOverrideSpellDataStore);
LOAD_DB2(sPhaseXPhaseGroupStore);
LOAD_DB2(sQuestMoneyRewardStore);
Expand Down Expand Up @@ -349,6 +355,27 @@ void DB2Manager::LoadStores(std::string const& dataPath)
for (NameGenEntry const* entry : sNameGenStore)
_nameGenData[entry->Race][entry->Sex].push_back(entry);

for (NamesProfanityEntry const* namesProfanity : sNamesProfanityStore)
{
ASSERT(namesProfanity->Language < TOTAL_LOCALES || namesProfanity->Language == -1);
if (namesProfanity->Language != -1)
_nameValidators[namesProfanity->Language].emplace_back(namesProfanity->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize);
else
for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
_nameValidators[i].emplace_back(namesProfanity->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize);
}

for (NamesReservedEntry const* namesReserved : sNamesReservedStore)
_nameValidators[TOTAL_LOCALES].emplace_back(namesReserved->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize);

for (NamesReservedLocaleEntry const* namesReserved : sNamesReservedLocaleStore)
{
ASSERT(!(namesReserved->LocaleMask & ~((1 << TOTAL_LOCALES) - 1)));
for (uint32 i = 0; i < TOTAL_LOCALES; ++i)
if (namesReserved->LocaleMask & (1 << i))
_nameValidators[i].emplace_back(namesReserved->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize);
}

for (PhaseXPhaseGroupEntry const* group : sPhaseXPhaseGroupStore)
if (PhaseEntry const* phase = sPhaseStore.LookupEntry(group->PhaseID))
_phasesByGroup[group->PhaseGroupID].insert(phase->ID);
Expand Down Expand Up @@ -711,6 +738,20 @@ DB2Manager::MountTypeXCapabilitySet const* DB2Manager::GetMountCapabilities(uint
return nullptr;
}

ResponseCodes DB2Manager::ValidateName(std::string const& name, LocaleConstant locale) const
{
for (boost::regex const& regex : _nameValidators[locale])
if (boost::regex_search(name, regex))
return CHAR_NAME_PROFANE;

// regexes at TOTAL_LOCALES are loaded from NamesReserved which is not locale specific
for (boost::regex const& regex : _nameValidators[TOTAL_LOCALES])
if (boost::regex_search(name, regex))
return CHAR_NAME_RESERVED;

return CHAR_NAME_SUCCESS;
}

std::vector<QuestPackageItemEntry const*> const* DB2Manager::GetQuestPackageItems(uint32 questPackageID) const
{
auto itr = _questPackages.find(questPackageID);
Expand Down
4 changes: 4 additions & 0 deletions src/server/game/DataStores/DB2Stores.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "DB2Store.h"
#include "DB2Structure.h"
#include "SharedDefines.h"
#include <boost/regex.hpp>
#include <array>

extern DB2Storage<AuctionHouseEntry> sAuctionHouseStore;
Expand Down Expand Up @@ -142,6 +143,7 @@ class DB2Manager
typedef std::set<MountTypeXCapabilityEntry const*, MountTypeXCapabilityEntryComparator> MountTypeXCapabilitySet;
typedef std::unordered_map<uint32, MountTypeXCapabilitySet> MountCapabilitiesByTypeContainer;
typedef std::unordered_map<uint32, std::array<std::vector<NameGenEntry const*>, 2>> NameGenContainer;
typedef std::array<std::vector<boost::regex>, TOTAL_LOCALES + 1> NameValidationRegexContainer;
typedef std::unordered_map<uint32, std::set<uint32>> PhaseGroupContainer;
typedef std::unordered_map<uint32, std::vector<QuestPackageItemEntry const*>> QuestPackageItemContainer;
typedef std::unordered_map<uint32, std::vector<SpecializationSpellsEntry const*>> SpecializationSpellsContainer;
Expand Down Expand Up @@ -176,6 +178,7 @@ class DB2Manager
MountEntry const* GetMount(uint32 spellId) const;
MountEntry const* GetMountById(uint32 id) const;
MountTypeXCapabilitySet const* GetMountCapabilities(uint32 mountType) const;
ResponseCodes ValidateName(std::string const& name, LocaleConstant locale) const;
std::vector<QuestPackageItemEntry const*> const* GetQuestPackageItems(uint32 questPackageID) const;
uint32 GetQuestUniqueBitFlag(uint32 questId);
std::set<uint32> GetPhasesForGroup(uint32 group) const;
Expand All @@ -200,6 +203,7 @@ class DB2Manager
MountContainer _mountsBySpellId;
MountCapabilitiesByTypeContainer _mountCapabilitiesByType;
NameGenContainer _nameGenData;
NameValidationRegexContainer _nameValidators;
PhaseGroupContainer _phasesByGroup;
QuestPackageItemContainer _questPackages;
SpecializationSpellsContainer _specializationSpellsBySpec;
Expand Down
20 changes: 20 additions & 0 deletions src/server/game/DataStores/DB2Structure.h
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,26 @@ struct NameGenEntry
uint32 Sex; // 3
};

struct NamesProfanityEntry
{
uint32 ID; // 0
char const* Name; // 1
int32 Language; // 2
};

struct NamesReservedEntry
{
uint32 ID; // 0
char const* Name; // 1
};

struct NamesReservedLocaleEntry
{
uint32 ID; // 0
char const* Name; // 1
uint32 LocaleMask; // 2
};

#define MAX_OVERRIDE_SPELL 10

struct OverrideSpellDataEntry
Expand Down
3 changes: 3 additions & 0 deletions src/server/game/DataStores/DB2fmt.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ char const MountCapabilityFormat[] = "niiiiiii";
char const MountFormat[] = "niiiisssii";
char const MountTypeXCapabilityFormat[] = "niii";
char const NameGenFormat[] = "nsii";
char const NamesProfanityFormat[] = "nSi";
char const NamesReservedFormat[] = "nS";
char const NamesReservedLocaleFormat[] = "nSi";
char const OverrideSpellDataFormat[] = "niiiiiiiiiiii";
char const PhaseXPhaseGroupFormat[] = "nii";
char const QuestMoneyRewardFormat[] = "niiiiiiiiii";
Expand Down
2 changes: 1 addition & 1 deletion src/server/game/Entities/Player/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16717,7 +16717,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
m_name = fields[2].GetString();

// check name limitations
if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS ||
if (ObjectMgr::CheckPlayerName(m_name, GetSession()->GetSessionDbcLocale()) != CHAR_NAME_SUCCESS ||
(!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME) &&
sObjectMgr->IsReservedName(m_name)))
{
Expand Down
4 changes: 2 additions & 2 deletions src/server/game/Globals/ObjectMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7509,7 +7509,7 @@ bool isValidString(const std::wstring& wstr, uint32 strictMask, bool numericOrSp
return false;
}

ResponseCodes ObjectMgr::CheckPlayerName(const std::string& name, bool create)
ResponseCodes ObjectMgr::CheckPlayerName(std::string const& name, LocaleConstant locale, bool create /*= false*/)
{
std::wstring wname;
if (!Utf8toWStr(name, wname))
Expand All @@ -7531,7 +7531,7 @@ ResponseCodes ObjectMgr::CheckPlayerName(const std::string& name, bool create)
if (wname[i] == wname[i-1] && wname[i] == wname[i-2])
return CHAR_NAME_THREE_CONSECUTIVE;

return CHAR_NAME_SUCCESS;
return sDB2Manager.ValidateName(name, locale);
}

bool ObjectMgr::IsValidCharterName(const std::string& name)
Expand Down
2 changes: 1 addition & 1 deletion src/server/game/Globals/ObjectMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1229,7 +1229,7 @@ class ObjectMgr
bool IsReservedName(std::string const& name) const;

// name with valid structure and symbols
static ResponseCodes CheckPlayerName(std::string const& name, bool create = false);
static ResponseCodes CheckPlayerName(std::string const& name, LocaleConstant locale, bool create = false);
static PetNameInvalidReason CheckPetName(std::string const& name);
static bool IsValidCharterName(std::string const& name);

Expand Down
8 changes: 4 additions & 4 deletions src/server/game/Handlers/CharacterHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPackets::Character::CreateCharact
}

// check name limitations
ResponseCodes res = ObjectMgr::CheckPlayerName(charCreate.CreateInfo->Name, true);
ResponseCodes res = ObjectMgr::CheckPlayerName(charCreate.CreateInfo->Name, GetSessionDbcLocale(), true);
if (res != CHAR_NAME_SUCCESS)
{
SendCharCreate(res);
Expand Down Expand Up @@ -1271,7 +1271,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPackets::Character::CharacterRena
return;
}

ResponseCodes res = ObjectMgr::CheckPlayerName(request.RenameInfo->NewName, true);
ResponseCodes res = ObjectMgr::CheckPlayerName(request.RenameInfo->NewName, GetSessionDbcLocale(), true);
if (res != CHAR_NAME_SUCCESS)
{
SendCharRename(res, request.RenameInfo.get());
Expand Down Expand Up @@ -1567,7 +1567,7 @@ void WorldSession::HandleCharCustomizeCallback(PreparedQueryResult result, World
return;
}

ResponseCodes res = ObjectMgr::CheckPlayerName(customizeInfo->CharName, true);
ResponseCodes res = ObjectMgr::CheckPlayerName(customizeInfo->CharName, GetSessionDbcLocale(), true);
if (res != CHAR_NAME_SUCCESS)
{
SendCharCustomize(res, customizeInfo);
Expand Down Expand Up @@ -1811,7 +1811,7 @@ void WorldSession::HandleCharRaceOrFactionChangeCallback(PreparedQueryResult res
return;
}

ResponseCodes res = ObjectMgr::CheckPlayerName(factionChangeInfo->Name, true);
ResponseCodes res = ObjectMgr::CheckPlayerName(factionChangeInfo->Name, GetSessionDbcLocale(), true);
if (res != CHAR_NAME_SUCCESS)
{
SendCharFactionChange(res, factionChangeInfo);
Expand Down
2 changes: 1 addition & 1 deletion src/server/game/Tools/PlayerDump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ DumpReturn PlayerDumpReader::LoadDump(std::string const& file, uint32 account, s
if (!normalizePlayerName(name))
name.clear();

if (ObjectMgr::CheckPlayerName(name, true) == CHAR_NAME_SUCCESS)
if (ObjectMgr::CheckPlayerName(name, sWorld->GetDefaultDbcLocale(), true) == CHAR_NAME_SUCCESS)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHECK_NAME);
stmt->setString(0, name);
Expand Down
4 changes: 2 additions & 2 deletions src/server/scripts/Commands/cs_character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ class character_commandscript : public CommandScript
return false;
}

if (ObjectMgr::CheckPlayerName(newName, true) != CHAR_NAME_SUCCESS)
if (ObjectMgr::CheckPlayerName(newName, target ? target->GetSession()->GetSessionDbcLocale() : sWorld->GetDefaultDbcLocale(), true) != CHAR_NAME_SUCCESS)
{
handler->SendSysMessage(LANG_BAD_VALUE);
handler->SetSentErrorMessage(true);
Expand Down Expand Up @@ -896,7 +896,7 @@ class character_commandscript : public CommandScript
return false;
}

if (ObjectMgr::CheckPlayerName(name, true) != CHAR_NAME_SUCCESS)
if (ObjectMgr::CheckPlayerName(name, sWorld->GetDefaultDbcLocale(), true) != CHAR_NAME_SUCCESS)
{
handler->PSendSysMessage(LANG_INVALID_CHARACTER_NAME);
handler->SetSentErrorMessage(true);
Expand Down
Loading

0 comments on commit b564c10

Please sign in to comment.