aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormegamage <none@none>2009-08-27 19:57:35 -0500
committermegamage <none@none>2009-08-27 19:57:35 -0500
commit90b55ff7b15bb243fce40ec733630bc605bf04a9 (patch)
treeb0381baf2443edf45173b1fb9e77c6f6b12f05ff /src
parent82bd15b7beb92d57e3925781581d62425d675398 (diff)
[8424] Added support for strict chatmessage validation Author: arrai
--HG-- branch : trunk
Diffstat (limited to 'src')
-rw-r--r--src/game/Chat.cpp537
-rw-r--r--src/game/Chat.h2
-rw-r--r--src/game/ChatHandler.cpp77
-rw-r--r--src/game/DBCStructure.h2
-rw-r--r--src/game/DBCfmt.h2
-rw-r--r--src/game/LootMgr.cpp2
-rw-r--r--src/game/SharedDefines.h24
-rw-r--r--src/game/Unit.cpp2
-rw-r--r--src/game/World.cpp2
-rw-r--r--src/game/World.h2
-rw-r--r--src/game/WorldSession.h1
-rw-r--r--src/trinitycore/trinitycore.conf.dist15
12 files changed, 630 insertions, 38 deletions
diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp
index a06bcf27fdd..6e391cca247 100644
--- a/src/game/Chat.cpp
+++ b/src/game/Chat.cpp
@@ -34,6 +34,7 @@
#include "Opcodes.h"
#include "Player.h"
#include "UpdateMask.h"
+#include "SpellMgr.h"
// Supported shift-links (client generated and server side)
// |color|Hachievement:achievement_id:player_guid:0:0:0:0:0:0:0:0|h[name]|h|r
@@ -1077,6 +1078,542 @@ int ChatHandler::ParseCommands(const char* text)
return 1;
}
+bool ChatHandler::isValidChatMessage(const char* message)
+{
+/*
+
+valid examples:
+|cffa335ee|Hitem:812:0:0:0:0:0:0:0:70|h[Glowing Brightwood Staff]|h|r
+|cff808080|Hquest:2278:47|h[The Platinum Discs]|h|r
+|cffffd000|Htrade:4037:1:150:1:6AAAAAAAAAAAAAAAAAAAAAAOAADAAAAAAAAAAAAAAAAIAAAAAAAAA|h[Engineering]|h|r
+|cff4e96f7|Htalent:2232:-1|h[Taste for Blood]|h|r
+|cff71d5ff|Hspell:21563|h[Command]|h|r
+|cffffd000|Henchant:3919|h[Engineering: Rough Dynamite]|h|r
+|cffffff00|Hachievement:546:0000000000000001:0:0:0:-1:0:0:0:0|h[Safe Deposit]|h|r
+|cff66bbff|Hglyph:21:762|h[Glyph of Bladestorm]|h|r
+
+| will be escaped to ||
+*/
+
+ if(strlen(message) > 255)
+ return false;
+
+ const char validSequence[6] = "cHhhr";
+ const char* validSequenceIterator = validSequence;
+
+ // more simple checks
+ if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) < 3)
+ {
+ const std::string validCommands = "cHhr|";
+
+ while(*message)
+ {
+ // find next pipe command
+ message = strchr(message, '|');
+
+ if(!message)
+ return true;
+
+ ++message;
+ char commandChar = *message;
+ if(validCommands.find(commandChar) == std::string::npos)
+ return false;
+
+ ++message;
+ // validate sequence
+ if(sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) == 2)
+ {
+ if(commandChar == *validSequenceIterator)
+ {
+ if (validSequenceIterator == validSequence+4)
+ validSequenceIterator = validSequence;
+ else
+ ++validSequenceIterator;
+ }
+ else
+ return false;
+ }
+ }
+ return true;
+ }
+
+ std::istringstream reader(message);
+ char buffer[256];
+
+ uint32 color;
+
+ ItemPrototype const* linkedItem;
+ Quest const* linkedQuest;
+ SpellEntry const *linkedSpell;
+ AchievementEntry const* linkedAchievement;
+
+ while(!reader.eof())
+ {
+ if (validSequence == validSequenceIterator)
+ {
+ linkedItem = NULL;
+ linkedQuest = NULL;
+ linkedSpell = NULL;
+ linkedAchievement = NULL;
+
+ reader.ignore(255, '|');
+ }
+ else if(reader.get() != '|')
+ {
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage sequence aborted unexpectedly");
+#endif
+ return false;
+ }
+
+ // pipe has always to be followed by at least one char
+ if ( reader.peek() == '\0')
+ {
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage pipe followed by \\0");
+#endif
+ return false;
+ }
+
+ // no further pipe commands
+ if (reader.eof())
+ break;
+
+ char commandChar;
+ reader >> commandChar;
+
+ // | in normal messages is escaped by ||
+ if (commandChar != '|')
+ {
+ if(commandChar == *validSequenceIterator)
+ {
+ if (validSequenceIterator == validSequence+4)
+ validSequenceIterator = validSequence;
+ else
+ ++validSequenceIterator;
+ }
+ else
+ {
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage invalid sequence, expected %c but got %c", *validSequenceIterator, commandChar);
+#endif
+ return false;
+ }
+ }
+ else if(validSequence != validSequenceIterator)
+ {
+ // no escaped pipes in sequences
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage got escaped pipe in sequence");
+#endif
+ return false;
+ }
+
+ switch (commandChar)
+ {
+ case 'c':
+ color = 0;
+ // validate color, expect 8 hex chars
+ for(int i=0; i<8; i++)
+ {
+ char c;
+ reader >> c;
+ if(!c)
+ {
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage got \\0 while reading color in |c command");
+#endif
+ return false;
+ }
+
+ color <<= 4;
+ // check for hex char
+ if(c >= '0' && c <='9')
+ {
+ color |= c-'0';
+ continue;
+ }
+ if(c >= 'a' && c <='f')
+ {
+ color |= 10+c-'a';
+ continue;
+ }
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage got non hex char '%c' while reading color", c);
+#endif
+ return false;
+ }
+ break;
+ case 'H':
+ // read chars up to colon = link type
+ reader.getline(buffer, 256, ':');
+
+ if (strcmp(buffer, "item") == 0)
+ {
+ // read item entry
+ reader.getline(buffer, 256, ':');
+
+ linkedItem= objmgr.GetItemPrototype(atoi(buffer));
+ if(!linkedItem)
+ {
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage got invalid itemID %u in |item command", atoi(buffer));
+#endif
+ return false;
+ }
+
+ if (color != ItemQualityColors[linkedItem->Quality])
+ {
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage linked item has color %u, but user claims %u", ItemQualityColors[linkedItem->Quality],
+ color);
+#endif
+ return false;
+ }
+
+ char c = reader.peek();
+
+ // ignore enchants etc.
+ while(c >='0' && c <='9' || c==':')
+ {
+ reader.ignore(1);
+ c = reader.peek();
+ }
+ }
+ else if(strcmp(buffer, "quest") == 0)
+ {
+ // no color check for questlinks, each client will adapt it anyway
+ uint32 questid= 0;
+ // read questid
+ char c = reader.peek();
+ while(c >='0' && c<='9')
+ {
+ reader.ignore(1);
+ questid *= 10;
+ questid += c-'0';
+ c = reader.peek();
+ }
+
+ linkedQuest = objmgr.GetQuestTemplate(questid);
+
+ if(!linkedQuest)
+ {
+#ifdef MANOGS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage Questtemplate %u not found", questid);
+#endif
+ return false;
+ }
+ c = reader.peek();
+ // level
+ while(c !='|' && c!='\0')
+ {
+ reader.ignore(1);
+ c = reader.peek();
+ }
+ }
+ else if(strcmp(buffer, "trade") == 0)
+ {
+ if(color != CHAT_LINK_COLOR_TRADE)
+ return false;
+
+ // read spell entry
+ reader.getline(buffer, 256, ':');
+ linkedSpell = sSpellStore.LookupEntry(atoi(buffer));
+ if(!linkedSpell)
+ return false;
+
+ char c = reader.peek();
+ // base64 encoded stuff
+ while(c !='|' && c!='\0')
+ {
+ reader.ignore(1);
+ c = reader.peek();
+ }
+ }
+ else if(strcmp(buffer, "talent") == 0)
+ {
+ // talent links are always supposed to be blue
+ if(color != CHAT_LINK_COLOR_TALENT)
+ return false;
+
+ // read talent entry
+ reader.getline(buffer, 256, ':');
+ TalentEntry const *talentInfo = sTalentStore.LookupEntry(atoi(buffer));
+ if(!talentInfo)
+ return false;
+
+ linkedSpell = sSpellStore.LookupEntry(talentInfo->RankID[0]);
+ if(!linkedSpell)
+ return false;
+
+ char c = reader.peek();
+ // skillpoints? whatever, drop it
+ while(c !='|' && c!='\0')
+ {
+ reader.ignore(1);
+ c = reader.peek();
+ }
+ }
+ else if(strcmp(buffer, "spell") == 0)
+ {
+ if(color != CHAT_LINK_COLOR_SPELL)
+ return false;
+
+ uint32 spellid = 0;
+ // read spell entry
+ char c = reader.peek();
+ while(c >='0' && c<='9')
+ {
+ reader.ignore(1);
+ spellid *= 10;
+ spellid += c-'0';
+ c = reader.peek();
+ }
+ linkedSpell = sSpellStore.LookupEntry(spellid);
+ if(!linkedSpell)
+ return false;
+ }
+ else if(strcmp(buffer, "enchant") == 0)
+ {
+ if(color != CHAT_LINK_COLOR_ENCHANT)
+ return false;
+
+ uint32 spellid = 0;
+ // read spell entry
+ char c = reader.peek();
+ while(c >='0' && c<='9')
+ {
+ reader.ignore(1);
+ spellid *= 10;
+ spellid += c-'0';
+ c = reader.peek();
+ }
+ linkedSpell = sSpellStore.LookupEntry(spellid);
+ if(!linkedSpell)
+ return false;
+ }
+ else if(strcmp(buffer, "achievement") == 0)
+ {
+ if(color != CHAT_LINK_COLOR_ACHIEVEMENT)
+ return false;
+ reader.getline(buffer, 256, ':');
+ uint32 achievementId = atoi(buffer);
+ linkedAchievement = sAchievementStore.LookupEntry(achievementId);
+
+ if(!linkedAchievement)
+ return false;
+
+ char c = reader.peek();
+ // skip progress
+ while(c !='|' && c!='\0')
+ {
+ reader.ignore(1);
+ c = reader.peek();
+ }
+ }
+ else if(strcmp(buffer, "glyph") == 0)
+ {
+ if(color != CHAT_LINK_COLOR_GLYPH)
+ return false;
+
+ // first id is slot, drop it
+ reader.getline(buffer, 256, ':');
+ uint32 glyphId = 0;
+ char c = reader.peek();
+ while(c>='0' && c <='9')
+ {
+ glyphId *= 10;
+ glyphId += c-'0';
+ reader.ignore(1);
+ c = reader.peek();
+ }
+ GlyphPropertiesEntry const* glyph = sGlyphPropertiesStore.LookupEntry(glyphId);
+ if(!glyph)
+ return false;
+
+ linkedSpell = sSpellStore.LookupEntry(glyph->SpellId);
+
+ if(!linkedSpell)
+ return false;
+ }
+ else
+ {
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage user sent unsupported link type '%s'", buffer);
+#endif
+ return false;
+ }
+ break;
+ case 'h':
+ // if h is next element in sequence, this one must contain the linked text :)
+ if(*validSequenceIterator == 'h')
+ {
+ // links start with '['
+ if(reader.get() != '[')
+ {
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage link caption doesn't start with '['");
+#endif
+ return false;
+ }
+ reader.getline(buffer, 256, ']');
+
+ // verify the link name
+ if (linkedSpell)
+ {
+ // spells with that flag have a prefix of "$PROFESSION: "
+ if(linkedSpell->Attributes & SPELL_ATTR_TRADESPELL)
+ {
+ // lookup skillid
+ SkillLineAbilityMap::const_iterator lower = spellmgr.GetBeginSkillLineAbilityMap(linkedSpell->Id);
+ SkillLineAbilityMap::const_iterator upper = spellmgr.GetEndSkillLineAbilityMap(linkedSpell->Id);
+
+ if(lower == upper)
+ {
+ return false;
+ }
+
+ SkillLineAbilityEntry const *skillInfo = lower->second;
+
+ if (!skillInfo)
+ {
+ return false;
+ }
+
+ SkillLineEntry const *skillLine = sSkillLineStore.LookupEntry(skillInfo->skillId);
+ if(!skillLine)
+ {
+ return false;
+ }
+
+ for(uint8 i=0; i<MAX_LOCALE; ++i)
+ {
+ uint32 skillLineNameLength = strlen(skillLine->name[i]);
+ if(skillLineNameLength > 0 && strncmp(skillLine->name[i], buffer, skillLineNameLength) == 0)
+ {
+ // found the prefix, remove it to perform spellname validation below
+ // -2 = strlen(": ")
+ uint32 spellNameLength = strlen(buffer)-skillLineNameLength-2;
+ memmove(buffer, buffer+skillLineNameLength+2, spellNameLength+1);
+ }
+ }
+ }
+ bool foundName = false;
+ for(uint8 i=0; i<MAX_LOCALE; ++i)
+ {
+ if(*linkedSpell->SpellName[i] && strcmp(linkedSpell->SpellName[i], buffer) == 0)
+ {
+ foundName = true;
+ break;
+ }
+ }
+ if(!foundName)
+ return false;
+ }
+ else if(linkedQuest)
+ {
+ if (linkedQuest->GetTitle() != buffer)
+ {
+ QuestLocale const *ql = objmgr.GetQuestLocale(linkedQuest->GetQuestId());
+
+ if(!ql)
+ {
+#ifdef MANOGS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage default questname didn't match and there is no locale");
+#endif
+ return false;
+ }
+
+ bool foundName = false;
+ for(uint8 i=0; i<ql->Title.size(); i++)
+ {
+ if(ql->Title[i] == buffer)
+ {
+ foundName = true;
+ break;
+ }
+ }
+ if(!foundName)
+ {
+#ifdef MANOGS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage no quest locale title matched")
+#endif
+ return false;
+ }
+ }
+ }
+ else if(linkedItem)
+ {
+ if(strcmp(linkedItem->Name1, buffer) != 0)
+ {
+ ItemLocale const *il = objmgr.GetItemLocale(linkedItem->ItemId);
+
+ if(!il)
+ {
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage linked item name doesn't is wrong and there is no localization");
+#endif
+ return false;
+ }
+
+ bool foundName = false;
+ for(uint8 i=0; i<il->Name.size(); ++i)
+ {
+ if(il->Name[i] == buffer)
+ {
+ foundName = true;
+ break;
+ }
+ }
+ if(!foundName)
+ {
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage linked item name wasn't found in any localization");
+#endif
+ return false;
+ }
+ }
+ }
+ else if(linkedAchievement)
+ {
+ bool foundName = false;
+ for(uint8 i=0; i<MAX_LOCALE; ++i)
+ {
+ if(*linkedAchievement->name[i], strcmp(linkedAchievement->name[i], buffer) == 0)
+ {
+ foundName = true;
+ break;
+ }
+ }
+ if(!foundName)
+ return false;
+ }
+ // that place should never be reached - if nothing linked has been set in |H
+ // it will return false before
+ else
+ return false;
+ }
+ break;
+ case 'r':
+ case '|':
+ // no further payload
+ break;
+ default:
+#ifdef MANGOS_DEBUG
+ sLog.outBasic("ChatHandler::isValidChatMessage got invalid command |%c", commandChar);
+#endif
+ return false;
+ }
+ }
+
+ // check if every opened sequence was also closed properly
+#ifdef MANGOS_DEBUG
+ if(validSequence != validSequenceIterator)
+ sLog.outBasic("ChatHandler::isValidChatMessage EOF in active sequence");
+#endif
+ return validSequence == validSequenceIterator;
+}
+
bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd)
{
std::string list;
diff --git a/src/game/Chat.h b/src/game/Chat.h
index 308698fc04f..f7b948c2cea 100644
--- a/src/game/Chat.h
+++ b/src/game/Chat.h
@@ -74,6 +74,8 @@ class ChatHandler
int ParseCommands(const char* text);
static ChatCommand* getCommandTable();
+
+ bool isValidChatMessage(const char* msg);
protected:
explicit ChatHandler() : m_session(NULL) {} // for CLI subclass
diff --git a/src/game/ChatHandler.cpp b/src/game/ChatHandler.cpp
index 106e445c245..082a4a6beac 100644
--- a/src/game/ChatHandler.cpp
+++ b/src/game/ChatHandler.cpp
@@ -40,6 +40,28 @@
#include "SpellAuras.h"
#include "Util.h"
+bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang)
+{
+ if (lang != LANG_ADDON)
+ {
+ // strip invisible characters for non-addon messages
+ if(sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
+ stripLineInvisibleChars(msg);
+
+ if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && GetSecurity() < SEC_MODERATOR
+ && !ChatHandler(this).isValidChatMessage(msg.c_str()))
+ {
+ sLog.outError("Player %s (GUID: %u) sent a chatmessage with an invalid link: %s", GetPlayer()->GetName(),
+ GetPlayer()->GetGUIDLow(), msg.c_str());
+ if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK))
+ KickPlayer();
+ return false;
+ }
+ }
+
+ return true;
+}
+
void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
{
uint32 type;
@@ -177,9 +199,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
- // strip invisible characters for non-addon messages
- if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
- stripLineInvisibleChars(msg);
+ if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
+ return;
if(msg.empty())
break;
@@ -198,9 +219,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
recv_data >> to;
recv_data >> msg;
- // strip invisible characters for non-addon messages
- if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
- stripLineInvisibleChars(msg);
+ if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
+ return;
if(msg.empty())
break;
@@ -257,9 +277,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
- // strip invisible characters for non-addon messages
- if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
- stripLineInvisibleChars(msg);
+ if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
+ return;
if(msg.empty())
break;
@@ -290,9 +309,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
- // strip invisible characters for non-addon messages
- if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
- stripLineInvisibleChars(msg);
+ if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
+ return;
if(msg.empty())
break;
@@ -328,9 +346,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
- // strip invisible characters for non-addon messages
- if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
- stripLineInvisibleChars(msg);
+ if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
+ return;
if(msg.empty())
break;
@@ -358,9 +375,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
- // strip invisible characters for non-addon messages
- if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
- stripLineInvisibleChars(msg);
+ if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
+ return;
if(msg.empty())
break;
@@ -390,9 +406,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
- // strip invisible characters for non-addon messages
- if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
- stripLineInvisibleChars(msg);
+ if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
+ return;
if(msg.empty())
break;
@@ -415,9 +430,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
std::string msg="";
recv_data >> msg;
- // strip invisible characters for non-addon messages
- if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
- stripLineInvisibleChars(msg);
+ if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
+ return;
if(msg.empty())
break;
@@ -441,9 +455,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
std::string msg="";
recv_data >> msg;
- // strip invisible characters for non-addon messages
- if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
- stripLineInvisibleChars(msg);
+ if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
+ return;
if(msg.empty())
break;
@@ -467,9 +480,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
std::string msg="";
recv_data >> msg;
- // strip invisible characters for non-addon messages
- if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
- stripLineInvisibleChars(msg);
+ if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
+ return;
if(msg.empty())
break;
@@ -495,9 +507,8 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
recv_data >> msg;
- // strip invisible characters for non-addon messages
- if (lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
- stripLineInvisibleChars(msg);
+ if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
+ return;
if(msg.empty())
break;
diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h
index ca903346815..c0af3a9be00 100644
--- a/src/game/DBCStructure.h
+++ b/src/game/DBCStructure.h
@@ -44,7 +44,7 @@ struct AchievementEntry
uint32 factionFlag; // 1 -1=all, 0=horde, 1=alliance
uint32 mapID; // 2 -1=none
//uint32 parentAchievement; // 3 its Achievement parent (can`t start while parent uncomplete, use its Criteria if don`t have own, use its progress on begin)
- //char *name[16]; // 4-19
+ char *name[16]; // 4-19
//uint32 name_flags; // 20
//char *description[16]; // 21-36
//uint32 desc_flags; // 37
diff --git a/src/game/DBCfmt.h b/src/game/DBCfmt.h
index 91fc4e5cc01..290514872f9 100644
--- a/src/game/DBCfmt.h
+++ b/src/game/DBCfmt.h
@@ -21,7 +21,7 @@
#ifndef MANGOS_DBCSFRM_H
#define MANGOS_DBCSFRM_H
-const char Achievementfmt[]="niixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii";
+const char Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii";
const char AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiixix";
const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxxx";
const char AreaGroupEntryfmt[]="niiiiiii";
diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp
index b54d7b90774..df11d909dd0 100644
--- a/src/game/LootMgr.cpp
+++ b/src/game/LootMgr.cpp
@@ -1325,7 +1325,7 @@ void LoadLootTemplates_Spell()
{
// not report about not trainable spells (optionally supported by DB)
// ignore 61756 (Northrend Inscription Research (FAST QA VERSION) for example
- if (!(spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT) || (spellInfo->Attributes & SPELL_ATTR_UNK5))
+ if (!(spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT) || (spellInfo->Attributes & SPELL_ATTR_TRADESPELL))
{
LootTemplates_Spell.ReportNotExistedId(spell_id);
}
diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h
index 11108cedf80..e03239332c4 100644
--- a/src/game/SharedDefines.h
+++ b/src/game/SharedDefines.h
@@ -215,6 +215,18 @@ enum SpellCategory
SPELL_CATEGORY_DRINK = 59,
};
+const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = {
+ 0xff9d9d9d, //GREY
+ 0xffffffff, //WHITE
+ 0xff1eff00, //GREEN
+ 0xff0070dd, //BLUE
+ 0xffa335ee, //PURPLE
+ 0xffff8000, //ORANGE
+ 0xffe6cc80, //LIGHT YELLOW
+ 0xffe6cc80 //LIGHT YELLOW
+};
+
+
// ***********************************
// Spell Attributes definitions
// ***********************************
@@ -224,7 +236,7 @@ enum SpellCategory
#define SPELL_ATTR_ON_NEXT_SWING 0x00000004 // 2 on next swing
#define SPELL_ATTR_UNK3 0x00000008 // 3 not set in 3.0.3
#define SPELL_ATTR_UNK4 0x00000010 // 4
-#define SPELL_ATTR_UNK5 0x00000020 // 5 trade spells?
+#define SPELL_ATTR_TRADESPELL 0x00000020 // 5 trade spells, will be added by client to a sublist of profession spell
#define SPELL_ATTR_PASSIVE 0x00000040 // 6 Passive spell
#define SPELL_ATTR_UNK7 0x00000080 // 7 visible?
#define SPELL_ATTR_UNK8 0x00000100 // 8
@@ -2398,6 +2410,16 @@ enum ChatMsg
#define MAX_CHAT_MSG_TYPE 0x32
+enum ChatLinkColors
+{
+ CHAT_LINK_COLOR_TRADE = 0xffffd000, // orange
+ CHAT_LINK_COLOR_TALENT = 0xff4e96f7, // blue
+ CHAT_LINK_COLOR_SPELL = 0xff71d5ff, // bright blue
+ CHAT_LINK_COLOR_ENCHANT = 0xffffd000, // orange
+ CHAT_LINK_COLOR_ACHIEVEMENT = 0xffffff00,
+ CHAT_LINK_COLOR_GLYPH = 0xff66bbff
+};
+
// Values from ItemPetFood (power of (value-1) used for compare with CreatureFamilyEntry.petDietMask
enum PetDiet
{
diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp
index b19c88ba376..dc404b40abf 100644
--- a/src/game/Unit.cpp
+++ b/src/game/Unit.cpp
@@ -11563,7 +11563,7 @@ void Unit::ModSpellCastTime(SpellEntry const* spellProto, int32 & castTime, Spel
if(Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CASTING_TIME, castTime, spell);
- if( !(spellProto->Attributes & (SPELL_ATTR_UNK4|SPELL_ATTR_UNK5)) && spellProto->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && spellProto->SpellFamilyName)
+ if( !(spellProto->Attributes & (SPELL_ATTR_UNK4|SPELL_ATTR_TRADESPELL)) && spellProto->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && spellProto->SpellFamilyName)
castTime = int32( float(castTime) * GetFloatValue(UNIT_MOD_CAST_SPEED));
else
{
diff --git a/src/game/World.cpp b/src/game/World.cpp
index ecc90e2ced0..22cfebf8911 100644
--- a/src/game/World.cpp
+++ b/src/game/World.cpp
@@ -977,6 +977,8 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_TALENTS_INSPECTING] = sConfig.GetBoolDefault("TalentsInspecting", true);
m_configs[CONFIG_CHAT_FAKE_MESSAGE_PREVENTING] = sConfig.GetBoolDefault("ChatFakeMessagePreventing", false);
+ m_configs[CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY] = sConfig.GetIntDefault("ChatStrictLinkChecking.Severity", 0);
+ m_configs[CONFIG_CHAT_STRICT_LINK_CHECKING_KICK] = sConfig.GetIntDefault("ChatStrictLinkChecking.Kick", 0);
m_configs[CONFIG_CORPSE_DECAY_NORMAL] = sConfig.GetIntDefault("Corpse.Decay.NORMAL", 60);
m_configs[CONFIG_CORPSE_DECAY_RARE] = sConfig.GetIntDefault("Corpse.Decay.RARE", 300);
diff --git a/src/game/World.h b/src/game/World.h
index 1a7eded0535..e78bd35e10a 100644
--- a/src/game/World.h
+++ b/src/game/World.h
@@ -188,6 +188,8 @@ enum WorldConfigs
CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL,
CONFIG_TALENTS_INSPECTING,
CONFIG_CHAT_FAKE_MESSAGE_PREVENTING,
+ CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY,
+ CONFIG_CHAT_STRICT_LINK_CHECKING_KICK,
CONFIG_CORPSE_DECAY_NORMAL,
CONFIG_CORPSE_DECAY_RARE,
CONFIG_CORPSE_DECAY_ELITE,
diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h
index 27c0c3e472d..c549a2c87ad 100644
--- a/src/game/WorldSession.h
+++ b/src/game/WorldSession.h
@@ -552,6 +552,7 @@ class TRINITY_DLL_SPEC WorldSession
void HandlePushQuestToParty(WorldPacket& recvPacket);
void HandleQuestPushResult(WorldPacket& recvPacket);
+ bool processChatmessageFurtherAfterSecurityChecks(std::string&, uint32);
void HandleMessagechatOpcode(WorldPacket& recvPacket);
void HandleTextEmoteOpcode(WorldPacket& recvPacket);
void HandleChatIgnoredOpcode(WorldPacket& recvPacket);
diff --git a/src/trinitycore/trinitycore.conf.dist b/src/trinitycore/trinitycore.conf.dist
index fcf34b97b01..a0360f7e00c 100644
--- a/src/trinitycore/trinitycore.conf.dist
+++ b/src/trinitycore/trinitycore.conf.dist
@@ -988,6 +988,19 @@ ListenRange.Yell = 300
# Default: 0 (disible fake messages preventing)
# 1 (enabled fake messages preventing)
#
+# ChatStrictLinkChecking.Severity
+# Check chat messages for ingame links to spells, items, quests, achievements etc.
+# Default: 0 (disable link checking)
+# 1 (check if only valid pipe commands are used. This prevents posting pictures for example)
+# 2 (verifiy that pipe commands are used in a correct order)
+# 3 (check if color, entry and name don't contradict each other. For correct work, please assure
+# that you have extracted locale DBCs of every language specific client playing on this server.)
+#
+# ChatStrictLinkChecking.Kick
+# Defines, what should be done if a message is considered to contain invalid pipe commands.
+# Default: 0 (silently ignore message)
+# 1 (kick players who sent invalid formed messages)
+#
# ChatFlood.MessageCount
# Chat anti-flood protection, haste message count to activate protection
# Default: 10
@@ -1014,6 +1027,8 @@ ListenRange.Yell = 300
###################################################################################################################
ChatFakeMessagePreventing = 0
+ChatStrictLinkChecking.Severity = 0
+ChatStrictLinkChecking.Kick = 0
ChatFlood.MessageCount = 10
ChatFlood.MessageDelay = 1
ChatFlood.MuteTime = 10