mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/Chat: move chat link validation code into separate classes and improve some validation logic
Core/Misc: fix non-pch build
This commit is contained in:
@@ -34,37 +34,7 @@
|
||||
#include "UpdateMask.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "ScriptMgr.h"
|
||||
|
||||
#ifdef TRINITY_DEBUG
|
||||
#define LOG(...) sLog->outDebug(LOG_FILTER_CHATSYS, __VA_ARGS__);
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
|
||||
// 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
|
||||
// - client, item icon shift click, not used in server currently
|
||||
// |color|Harea:area_id|h[name]|h|r
|
||||
// |color|Hcreature:creature_guid|h[name]|h|r
|
||||
// |color|Hcreature_entry:creature_id|h[name]|h|r
|
||||
// |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r - client, at shift click in recipes list dialog
|
||||
// |color|Hgameevent:id|h[name]|h|r
|
||||
// |color|Hgameobject:go_guid|h[name]|h|r
|
||||
// |color|Hgameobject_entry:go_id|h[name]|h|r
|
||||
// |color|Hglyph:glyph_slot_id:glyph_prop_id|h[%s]|h|r - client, at shift click in glyphs dialog, GlyphSlot.dbc, GlyphProperties.dbc
|
||||
// |color|Hitem:item_id:perm_ench_id:gem1:gem2:gem3:0:0:0:0:reporter_level|h[name]|h|r
|
||||
// - client, item icon shift click
|
||||
// |color|Hitemset:itemset_id|h[name]|h|r
|
||||
// |color|Hplayer:name|h[name]|h|r - client, in some messages, at click copy only name instead link
|
||||
// |color|Hquest:quest_id:quest_level|h[name]|h|r - client, quest list name shift-click
|
||||
// |color|Hskill:skill_id|h[name]|h|r
|
||||
// |color|Hspell:spell_id|h[name]|h|r - client, spellbook spell icon shift-click
|
||||
// |color|Htalent:talent_id,rank|h[name]|h|r - client, talent icon shift-click
|
||||
// |color|Htaxinode:id|h[name]|h|r
|
||||
// |color|Htele:id|h[name]|h|r
|
||||
// |color|Htitle:id|h[name]|h|r
|
||||
// |color|Htrade:spell_id:cur_value:max_value:unk3int:unk3str|h[name]|h|r - client, spellbook profession icon shift-click
|
||||
#include "ChatLink.h"
|
||||
|
||||
bool ChatHandler::load_command_table = true;
|
||||
|
||||
@@ -850,47 +820,10 @@ int ChatHandler::ParseCommands(const char* text)
|
||||
return 1;
|
||||
}
|
||||
|
||||
inline uint32 ChatHandler::_ReadUInt32(std::istringstream& reader) const
|
||||
{
|
||||
uint32 res = 0;
|
||||
char c = reader.peek();
|
||||
while (c >='0' && c <= '9')
|
||||
{
|
||||
reader.ignore(1);
|
||||
res *= 10;
|
||||
res += c - '0';
|
||||
c = reader.peek();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
inline int32 ChatHandler::_ReadInt32(std::istringstream& reader) const
|
||||
{
|
||||
int32 res = 0;
|
||||
bool isNegative = false;
|
||||
char c = reader.peek();
|
||||
while ((c >= '0' && c <= '9') || c == '-')
|
||||
{
|
||||
if (c >='0' && c <= '9')
|
||||
{
|
||||
res *= 10;
|
||||
res += c - '0';
|
||||
}
|
||||
else if (c == '-')
|
||||
isNegative = true;
|
||||
reader.ignore(1);
|
||||
c = reader.peek();
|
||||
}
|
||||
if (isNegative)
|
||||
res = -res;
|
||||
return res;
|
||||
}
|
||||
|
||||
bool ChatHandler::isValidChatMessage(const char* message)
|
||||
{
|
||||
/*
|
||||
|
||||
valid examples:
|
||||
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
|
||||
@@ -906,12 +839,11 @@ valid examples:
|
||||
if (strlen(message) > 255)
|
||||
return false;
|
||||
|
||||
const char validSequence[6] = "cHhhr";
|
||||
const char* validSequenceIterator = validSequence;
|
||||
|
||||
// more simple checks
|
||||
if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) < 3)
|
||||
{
|
||||
const char validSequence[6] = "cHhhr";
|
||||
const char* validSequenceIterator = validSequence;
|
||||
const std::string validCommands = "cHhr|";
|
||||
|
||||
while (*message)
|
||||
@@ -933,7 +865,7 @@ valid examples:
|
||||
{
|
||||
if (commandChar == *validSequenceIterator)
|
||||
{
|
||||
if (validSequenceIterator == validSequence+4)
|
||||
if (validSequenceIterator == validSequence + 4)
|
||||
validSequenceIterator = validSequence;
|
||||
else
|
||||
++validSequenceIterator;
|
||||
@@ -945,539 +877,7 @@ valid examples:
|
||||
return true;
|
||||
}
|
||||
|
||||
std::istringstream reader(message);
|
||||
char buffer[256];
|
||||
|
||||
uint32 color = 0;
|
||||
|
||||
ItemPrototype const* linkedItem = NULL;
|
||||
Quest const* linkedQuest = NULL;
|
||||
SpellEntry const *linkedSpell = NULL;
|
||||
AchievementEntry const* linkedAchievement = NULL;
|
||||
ItemRandomPropertiesEntry const* itemProperty = NULL;
|
||||
ItemRandomSuffixEntry const* itemSuffix = NULL;
|
||||
|
||||
while (!reader.eof())
|
||||
{
|
||||
if (validSequence == validSequenceIterator)
|
||||
{
|
||||
linkedItem = NULL;
|
||||
linkedQuest = NULL;
|
||||
linkedSpell = NULL;
|
||||
linkedAchievement = NULL;
|
||||
itemProperty = NULL;
|
||||
itemSuffix = NULL;
|
||||
|
||||
reader.ignore(255, '|');
|
||||
}
|
||||
else if (reader.get() != '|')
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence aborted unexpectedly", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// pipe has always to be followed by at least one char
|
||||
if (reader.peek() == '\0')
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): pipe followed by '\\0'", reader.str().c_str());
|
||||
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
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): invalid sequence, expected '%c' but got '%c'", reader.str().c_str(), *validSequenceIterator, commandChar);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (validSequence != validSequenceIterator)
|
||||
{
|
||||
// no escaped pipes in sequences
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got escaped pipe in sequence", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (commandChar)
|
||||
{
|
||||
case 'c':
|
||||
color = 0;
|
||||
// validate color, expect 8 hex chars
|
||||
for (uint8 i = 0; i < 8; ++i)
|
||||
{
|
||||
char c;
|
||||
reader >> c;
|
||||
if (!c)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got \\0 while reading color in |c command", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
color <<= 4;
|
||||
// check for hex char
|
||||
if (c >= '0' && c <= '9')
|
||||
color |= c - '0';
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
color |= 10 + c - 'a';
|
||||
else
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got non hex char '%c' while reading color", reader.str().c_str(), c);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
// read chars up to colon = link type
|
||||
reader.getline(buffer, 256, ':');
|
||||
if (reader.eof())
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(buffer, "item") == 0)
|
||||
{
|
||||
// read item entry
|
||||
reader.getline(buffer, 256, ':');
|
||||
if (reader.eof())
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 itemEntry = atoi(buffer);
|
||||
linkedItem = ObjectMgr::GetItemPrototype(itemEntry);
|
||||
if (!linkedItem)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid itemEntry %u in |item command", reader.str().c_str(), itemEntry);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (color != ItemQualityColors[linkedItem->Quality])
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): linked item has color %u, but user claims %u", reader.str().c_str(), ItemQualityColors[linkedItem->Quality], color);
|
||||
return false;
|
||||
}
|
||||
|
||||
// The itementry is followed by several integers which describe an instance of this item
|
||||
const uint8 randomPropertyPosition = 6;
|
||||
|
||||
char c = '\0';
|
||||
int32 id = 0;
|
||||
for (uint8 i = 0; i < randomPropertyPosition; ++i)
|
||||
{
|
||||
id = _ReadInt32(reader);
|
||||
c = reader.get();
|
||||
if (c != ':')
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): invalid character '%c' found while reading item properties", reader.str().c_str(), c);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Validate random property
|
||||
if (id > 0)
|
||||
{
|
||||
itemProperty = sItemRandomPropertiesStore.LookupEntry(id);
|
||||
if (!itemProperty)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid item property id %u in |item command", reader.str().c_str(), id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (id < 0)
|
||||
{
|
||||
itemSuffix = sItemRandomSuffixStore.LookupEntry(-id);
|
||||
if (!itemSuffix)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid item suffix id %u in |item command", reader.str().c_str(), -id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// ignore other integers
|
||||
while ((c >= '0' && c <= '9') || c == ':')
|
||||
{
|
||||
reader.ignore(1);
|
||||
c = reader.peek();
|
||||
}
|
||||
}
|
||||
else if (strcmp(buffer, "quest") == 0)
|
||||
{
|
||||
// Quest Id
|
||||
uint32 questId = _ReadUInt32(reader);
|
||||
linkedQuest = sObjectMgr->GetQuestTemplate(questId);
|
||||
if (!linkedQuest)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): quest template %u not found", reader.str().c_str(), questId);
|
||||
return false;
|
||||
}
|
||||
// Delimiter
|
||||
char c = reader.peek();
|
||||
if (c != ':')
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): invalid quest link structure (':' expected, '%c' found)", reader.str().c_str(), c);
|
||||
return false;
|
||||
}
|
||||
reader.ignore(1);
|
||||
// Quest level
|
||||
int32 questLevel = _ReadInt32(reader);
|
||||
if (questLevel >= STRONG_MAX_LEVEL)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): quest level %d is too big", reader.str().c_str(), questLevel);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (strcmp(buffer, "trade") == 0)
|
||||
{
|
||||
if (color != CHAT_LINK_COLOR_TRADE)
|
||||
return false;
|
||||
|
||||
// Spell Id
|
||||
reader.getline(buffer, 256, ':');
|
||||
if (reader.eof())
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 spellId = atoi(buffer);
|
||||
linkedSpell = sSpellStore.LookupEntry(spellId);
|
||||
if (!linkedSpell)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |trade command", reader.str().c_str(), spellId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// base64 encoded stuff
|
||||
char c = reader.peek();
|
||||
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, ':');
|
||||
if (reader.eof())
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 talentId = atoi(buffer);
|
||||
TalentEntry const *talentInfo = sTalentStore.LookupEntry(talentId);
|
||||
if (!talentInfo)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid talent id %u in |talent command", reader.str().c_str(), talentId);
|
||||
return false;
|
||||
}
|
||||
|
||||
linkedSpell = sSpellStore.LookupEntry(talentInfo->RankID[0]);
|
||||
if (!linkedSpell)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |trade command", reader.str().c_str(), talentInfo->RankID[0]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// skillpoints? whatever, drop it
|
||||
char c = reader.peek();
|
||||
while (c != '|' && c != '\0')
|
||||
{
|
||||
reader.ignore(1);
|
||||
c = reader.peek();
|
||||
}
|
||||
}
|
||||
else if (strcmp(buffer, "spell") == 0)
|
||||
{
|
||||
if (color != CHAT_LINK_COLOR_SPELL)
|
||||
return false;
|
||||
|
||||
// Spell Id
|
||||
uint32 spellId = _ReadUInt32(reader);
|
||||
linkedSpell = sSpellStore.LookupEntry(spellId);
|
||||
if (!linkedSpell)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |spell command", reader.str().c_str(), spellId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (strcmp(buffer, "enchant") == 0)
|
||||
{
|
||||
if (color != CHAT_LINK_COLOR_ENCHANT)
|
||||
return false;
|
||||
|
||||
// Spell Id
|
||||
uint32 spellId = _ReadUInt32(reader);
|
||||
linkedSpell = sSpellStore.LookupEntry(spellId);
|
||||
if (!linkedSpell)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |enchant command", reader.str().c_str(), spellId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (strcmp(buffer, "achievement") == 0)
|
||||
{
|
||||
if (color != CHAT_LINK_COLOR_ACHIEVEMENT)
|
||||
return false;
|
||||
|
||||
// Achievemnt Id
|
||||
reader.getline(buffer, 256, ':');
|
||||
if (reader.eof())
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 achievementId = atoi(buffer);
|
||||
linkedAchievement = sAchievementStore.LookupEntry(achievementId);
|
||||
if (!linkedAchievement)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid achivement id %u in |achievement command", reader.str().c_str(), achievementId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip progress
|
||||
char c = reader.peek();
|
||||
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, ':');
|
||||
if (reader.eof())
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 glyphId = _ReadUInt32(reader);
|
||||
GlyphPropertiesEntry const* glyph = sGlyphPropertiesStore.LookupEntry(glyphId);
|
||||
if (!glyph)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid glyph id %u in |glyph command", reader.str().c_str(), glyphId);
|
||||
return false;
|
||||
}
|
||||
|
||||
linkedSpell = sSpellStore.LookupEntry(glyph->SpellId);
|
||||
if (!linkedSpell)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |glyph command", reader.str().c_str(), glyph->SpellId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): user sent unsupported link type '%s'", reader.str().c_str(), buffer);
|
||||
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() != '[')
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): link caption doesn't start with '['", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
reader.getline(buffer, 256, ']');
|
||||
if (reader.eof())
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// verify the link name
|
||||
if (linkedSpell)
|
||||
{
|
||||
// spells with that flag have a prefix of "$PROFESSION: "
|
||||
if (linkedSpell->Attributes & SPELL_ATTR0_TRADESPELL)
|
||||
{
|
||||
// lookup skillid
|
||||
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(linkedSpell->Id);
|
||||
if (bounds.first == bounds.second)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): skill line not found for spell %u", reader.str().c_str(), linkedSpell->Id);
|
||||
return false;
|
||||
}
|
||||
|
||||
SkillLineAbilityEntry const *skillInfo = bounds.first->second;
|
||||
if (!skillInfo)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): skill line ability not found for spell %u", reader.str().c_str(), linkedSpell->Id);
|
||||
return false;
|
||||
}
|
||||
|
||||
SkillLineEntry const *skillLine = sSkillLineStore.LookupEntry(skillInfo->skillId);
|
||||
if (!skillLine)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): skill line not found for skill %u", reader.str().c_str(), skillInfo->skillId);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < TOTAL_LOCALES; ++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 < TOTAL_LOCALES; ++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 = sObjectMgr->GetQuestLocale(linkedQuest->GetQuestId());
|
||||
if (!ql)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): default questname didn't match and no locales present for this quest (id: %u)", reader.str().c_str(), linkedQuest->GetQuestId());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool foundName = false;
|
||||
for (uint8 i=0; i<ql->Title.size(); i++)
|
||||
{
|
||||
if (ql->Title[i] == buffer)
|
||||
{
|
||||
foundName = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundName)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): linked quest (id: %u) name wasn't found in any localization", reader.str().c_str(), linkedQuest->GetQuestId());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (linkedItem)
|
||||
{
|
||||
char* const* suffix = itemSuffix ? itemSuffix->nameSuffix :
|
||||
(itemProperty ? itemProperty->nameSuffix : NULL);
|
||||
|
||||
std::string expectedName = std::string(linkedItem->Name1);
|
||||
if (suffix)
|
||||
{
|
||||
expectedName += " ";
|
||||
expectedName += suffix[LOCALE_enUS];
|
||||
}
|
||||
|
||||
if (expectedName != buffer)
|
||||
{
|
||||
ItemLocale const *il = sObjectMgr->GetItemLocale(linkedItem->ItemId);
|
||||
|
||||
bool foundName = false;
|
||||
for (uint8 dbIndex = LOCALE_koKR; dbIndex < TOTAL_LOCALES; ++dbIndex)
|
||||
{
|
||||
if (il == NULL || dbIndex >= il->Name.size())
|
||||
// using strange database/client combinations can lead to this case
|
||||
expectedName = linkedItem->Name1;
|
||||
else
|
||||
expectedName = il->Name[dbIndex];
|
||||
if (suffix)
|
||||
{
|
||||
expectedName += " ";
|
||||
expectedName += suffix[dbIndex];
|
||||
}
|
||||
if (expectedName == buffer)
|
||||
{
|
||||
foundName = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundName)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): linked item (id: %u) name wasn't found in any localization", reader.str().c_str(), linkedItem->ItemId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (linkedAchievement)
|
||||
{
|
||||
bool foundName = false;
|
||||
for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
|
||||
{
|
||||
if (*linkedAchievement->name[i] && strcmp(linkedAchievement->name[i], buffer) == 0)
|
||||
{
|
||||
foundName = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!foundName)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): linked achievement (id: %u) name wasn't found in any localization", reader.str().c_str(), linkedAchievement->ID);
|
||||
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:
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid command |%c", reader.str().c_str(), commandChar);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check if every opened sequence was also closed properly
|
||||
if (validSequence != validSequenceIterator)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): EOF in active sequence", reader.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return LinkExtractor(message).IsValidMessage();
|
||||
}
|
||||
|
||||
bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd)
|
||||
|
||||
@@ -374,8 +374,6 @@ class ChatHandler
|
||||
void HandleCharacterDeletedListHelper(DeletedInfoList const& foundList);
|
||||
void HandleCharacterDeletedRestoreHelper(DeletedInfo const& delInfo);
|
||||
|
||||
uint32 _ReadUInt32(std::istringstream& reader) const;
|
||||
int32 _ReadInt32(std::istringstream& reader) const;
|
||||
private:
|
||||
bool _HandleGMTicketResponseAppendCommand(const char* args, bool newLine);
|
||||
|
||||
|
||||
714
src/server/game/Chat/ChatLink.cpp
Normal file
714
src/server/game/Chat/ChatLink.cpp
Normal file
@@ -0,0 +1,714 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2011 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 "ChatLink.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
|
||||
#ifdef TRINITY_DEBUG
|
||||
#define LOG(...) sLog->outDebug(LOG_FILTER_CHATSYS, __VA_ARGS__);
|
||||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
|
||||
// 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
|
||||
// - client, item icon shift click, not used in server currently
|
||||
// |color|Harea:area_id|h[name]|h|r
|
||||
// |color|Hcreature:creature_guid|h[name]|h|r
|
||||
// |color|Hcreature_entry:creature_id|h[name]|h|r
|
||||
// |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r - client, at shift click in recipes list dialog
|
||||
// |color|Hgameevent:id|h[name]|h|r
|
||||
// |color|Hgameobject:go_guid|h[name]|h|r
|
||||
// |color|Hgameobject_entry:go_id|h[name]|h|r
|
||||
// |color|Hglyph:glyph_slot_id:glyph_prop_id|h[%s]|h|r - client, at shift click in glyphs dialog, GlyphSlot.dbc, GlyphProperties.dbc
|
||||
// |color|Hitem:item_id:perm_ench_id:gem1:gem2:gem3:0:0:0:0:reporter_level|h[name]|h|r
|
||||
// - client, item icon shift click
|
||||
// |color|Hitemset:itemset_id|h[name]|h|r
|
||||
// |color|Hplayer:name|h[name]|h|r - client, in some messages, at click copy only name instead link
|
||||
// |color|Hquest:quest_id:quest_level|h[name]|h|r - client, quest list name shift-click
|
||||
// |color|Hskill:skill_id|h[name]|h|r
|
||||
// |color|Hspell:spell_id|h[name]|h|r - client, spellbook spell icon shift-click
|
||||
// |color|Htalent:talent_id,rank|h[name]|h|r - client, talent icon shift-click
|
||||
// |color|Htaxinode:id|h[name]|h|r
|
||||
// |color|Htele:id|h[name]|h|r
|
||||
// |color|Htitle:id|h[name]|h|r
|
||||
// |color|Htrade:spell_id:cur_value:max_value:unk3int:unk3str|h[name]|h|r - client, spellbook profession icon shift-click
|
||||
|
||||
inline bool ReadUInt32(std::istringstream& iss, uint32& res)
|
||||
{
|
||||
iss >> std::dec >> res;
|
||||
return !iss.fail() && !iss.eof();
|
||||
}
|
||||
|
||||
inline bool ReadInt32(std::istringstream& iss, int32& res)
|
||||
{
|
||||
iss >> std::dec >> res;
|
||||
return !iss.fail() && !iss.eof();
|
||||
}
|
||||
|
||||
inline std::string ReadSkip(std::istringstream& iss, char term)
|
||||
{
|
||||
std::string res;
|
||||
char c = iss.peek();
|
||||
while (c != term && c != '\0')
|
||||
{
|
||||
res += c;
|
||||
iss.ignore(1);
|
||||
c = iss.peek();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
inline bool CheckDelimiter(std::istringstream& iss, char delimiter, const char* context)
|
||||
{
|
||||
char c = iss.peek();
|
||||
if (c != delimiter)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): invalid %s link structure ('%c' expected, '%c' found)", iss.str().c_str(), context, delimiter, c);
|
||||
return false;
|
||||
}
|
||||
iss.ignore(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool ReadHex(std::istringstream& iss, uint32& res, uint32 length)
|
||||
{
|
||||
std::istringstream::pos_type pos = iss.tellg();
|
||||
iss >> std::hex >> res;
|
||||
uint32 size = uint32(iss.gcount());
|
||||
if (length && uint32(iss.tellg() - pos) != length)
|
||||
return false;
|
||||
return !iss.fail() && !iss.eof();
|
||||
}
|
||||
|
||||
#define DELIMITER ':'
|
||||
#define PIPE_CHAR '|'
|
||||
|
||||
bool ChatLink::ValidateName(char* buffer, const char* context)
|
||||
{
|
||||
_name = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
// |color|Hitem:item_id:perm_ench_id:gem1:gem2:gem3:0:random_property:0:reporter_level|h[name]|h|r
|
||||
// |cffa335ee|Hitem:812:0:0:0:0:0:0:0:70|h[Glowing Brightwood Staff]|h|r
|
||||
bool ItemChatLink::Initialize(std::istringstream& iss)
|
||||
{
|
||||
// Read item entry
|
||||
uint32 itemEntry = 0;
|
||||
if (!ReadUInt32(iss, itemEntry))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading item entry", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Validate item
|
||||
_item = ObjectMgr::GetItemPrototype(itemEntry);
|
||||
if (!_item)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid itemEntry %u in |item command", iss.str().c_str(), itemEntry);
|
||||
return false;
|
||||
}
|
||||
// Validate item's color
|
||||
if (_color != ItemQualityColors[_item->Quality])
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): linked item has color %u, but user claims %u", iss.str().c_str(), ItemQualityColors[_item->Quality], _color);
|
||||
return false;
|
||||
}
|
||||
// Number of various item properties after item entry
|
||||
const uint8 propsCount = 8;
|
||||
const uint8 randomPropertyPosition = 5;
|
||||
for (uint8 index = 0; index < propsCount; ++index)
|
||||
{
|
||||
if (!CheckDelimiter(iss, DELIMITER, "item"))
|
||||
return false;
|
||||
|
||||
int32 id = 0;
|
||||
if (!ReadInt32(iss, id))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading item property (%u)", iss.str().c_str(), index);
|
||||
return false;
|
||||
}
|
||||
if (id && (index == randomPropertyPosition))
|
||||
{
|
||||
// Validate random property
|
||||
if (id > 0)
|
||||
{
|
||||
_property = sItemRandomPropertiesStore.LookupEntry(id);
|
||||
if (!_property)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid item property id %u in |item command", iss.str().c_str(), id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (id < 0)
|
||||
{
|
||||
_suffix = sItemRandomSuffixStore.LookupEntry(-id);
|
||||
if (!_suffix)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid item suffix id %u in |item command", iss.str().c_str(), -id);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
_data[index] = id;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline std::string ItemChatLink::_FormatName(uint8 index, ItemLocale const *locale, char* const* suffixStrings) const
|
||||
{
|
||||
std::stringstream ss;
|
||||
if (locale == NULL || index >= locale->Name.size())
|
||||
ss << _item->Name1;
|
||||
else
|
||||
ss << locale->Name[index];
|
||||
if (suffixStrings)
|
||||
ss << " " << suffixStrings[index];
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool ItemChatLink::ValidateName(char* buffer, const char* context)
|
||||
{
|
||||
ChatLink::ValidateName(buffer, context);
|
||||
|
||||
char* const* suffixStrings = _suffix ? _suffix->nameSuffix : (_property ? _property->nameSuffix : NULL);
|
||||
|
||||
bool res = (_FormatName(LOCALE_enUS, NULL, suffixStrings) == buffer);
|
||||
if (!res)
|
||||
{
|
||||
ItemLocale const *il = sObjectMgr->GetItemLocale(_item->ItemId);
|
||||
for (uint8 index = LOCALE_koKR; index < TOTAL_LOCALES; ++index)
|
||||
{
|
||||
if (_FormatName(index, il, suffixStrings) == buffer)
|
||||
{
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!res)
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): linked item (id: %u) name wasn't found in any localization", context, _item->ItemId);
|
||||
return res;
|
||||
}
|
||||
|
||||
// |color|Hquest:quest_id:quest_level|h[name]|h|r
|
||||
// |cff808080|Hquest:2278:47|h[The Platinum Discs]|h|r
|
||||
bool QuestChatLink::Initialize(std::istringstream& iss)
|
||||
{
|
||||
// Read quest id
|
||||
uint32 questId = 0;
|
||||
if (!ReadUInt32(iss, questId))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading quest entry", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Validate quest
|
||||
_quest = sObjectMgr->GetQuestTemplate(questId);
|
||||
if (!_quest)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): quest template %u not found", iss.str().c_str(), questId);
|
||||
return false;
|
||||
}
|
||||
// Check delimiter
|
||||
if (!CheckDelimiter(iss, DELIMITER, "quest"))
|
||||
return false;
|
||||
// Read quest level
|
||||
if (!ReadInt32(iss, _questLevel))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading quest level", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Validate quest level
|
||||
if (_questLevel >= STRONG_MAX_LEVEL)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): quest level %d is too big", iss.str().c_str(), _questLevel);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QuestChatLink::ValidateName(char* buffer, const char* context)
|
||||
{
|
||||
ChatLink::ValidateName(buffer, context);
|
||||
|
||||
bool res = (_quest->GetTitle() == buffer);
|
||||
if (!res)
|
||||
if (QuestLocale const *ql = sObjectMgr->GetQuestLocale(_quest->GetQuestId()))
|
||||
for (uint8 i = 0; i < ql->Title.size(); i++)
|
||||
if (ql->Title[i] == buffer)
|
||||
{
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
if (!res)
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): linked quest (id: %u) title wasn't found in any localization", context, _quest->GetQuestId());
|
||||
return res;
|
||||
}
|
||||
|
||||
// |color|Hspell:spell_id|h[name]|h|r
|
||||
// |cff71d5ff|Hspell:21563|h[Command]|h|r
|
||||
bool SpellChatLink::Initialize(std::istringstream& iss)
|
||||
{
|
||||
if (_color != CHAT_LINK_COLOR_SPELL)
|
||||
return false;
|
||||
// Read spell id
|
||||
uint32 spellId = 0;
|
||||
if (!ReadUInt32(iss, spellId))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading spell entry", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Validate spell
|
||||
_spell = sSpellStore.LookupEntry(spellId);
|
||||
if (!_spell)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |spell command", iss.str().c_str(), spellId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpellChatLink::ValidateName(char* buffer, const char* context)
|
||||
{
|
||||
ChatLink::ValidateName(buffer, context);
|
||||
|
||||
// spells with that flag have a prefix of "$PROFESSION: "
|
||||
if (_spell->Attributes & SPELL_ATTR0_TRADESPELL)
|
||||
{
|
||||
SkillLineAbilityMapBounds bounds = sSpellMgr->GetSkillLineAbilityMapBounds(_spell->Id);
|
||||
if (bounds.first == bounds.second)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): skill line not found for spell %u", context, _spell->Id);
|
||||
return false;
|
||||
}
|
||||
SkillLineAbilityEntry const *skillInfo = bounds.first->second;
|
||||
if (!skillInfo)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): skill line ability not found for spell %u", context, _spell->Id);
|
||||
return false;
|
||||
}
|
||||
SkillLineEntry const *skillLine = sSkillLineStore.LookupEntry(skillInfo->skillId);
|
||||
if (!skillLine)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): skill line not found for skill %u", context, skillInfo->skillId);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8 i = 0; i < TOTAL_LOCALES; ++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;
|
||||
memcpy(buffer, buffer + skillLineNameLength + 2, spellNameLength + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool res = false;
|
||||
for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
|
||||
if (*_spell->SpellName[i] && strcmp(_spell->SpellName[i], buffer) == 0)
|
||||
{
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!res)
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): linked spell (id: %u) name wasn't found in any localization", context, _spell->Id);
|
||||
return res;
|
||||
}
|
||||
|
||||
// |color|Hachievement:achievement_id:player_guid:0:0:0:0:0:0:0:0|h[name]|h|r
|
||||
// |cffffff00|Hachievement:546:0000000000000001:0:0:0:-1:0:0:0:0|h[Safe Deposit]|h|r
|
||||
bool AchievementChatLink::Initialize(std::istringstream& iss)
|
||||
{
|
||||
if (_color != CHAT_LINK_COLOR_ACHIEVEMENT)
|
||||
return false;
|
||||
// Read achievemnt Id
|
||||
uint32 achievementId = 0;
|
||||
if (!ReadUInt32(iss, achievementId))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement entry", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Validate achievement
|
||||
_achievement = sAchievementStore.LookupEntry(achievementId);
|
||||
if (!_achievement)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid achivement id %u in |achievement command", iss.str().c_str(), achievementId);
|
||||
return false;
|
||||
}
|
||||
// Check delimiter
|
||||
if (!CheckDelimiter(iss, DELIMITER, "achievement"))
|
||||
return false;
|
||||
// Read HEX
|
||||
if (!ReadHex(iss, _guid, 0))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): invalid hexadecimal number while reading char's guid", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Skip progress
|
||||
const uint8 propsCount = 8;
|
||||
for (uint8 index = 0; index < propsCount; ++index)
|
||||
{
|
||||
if (!CheckDelimiter(iss, DELIMITER, "achievement"))
|
||||
return false;
|
||||
|
||||
if (!ReadUInt32(iss, _data[index]))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement property (%u)", iss.str().c_str(), index);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AchievementChatLink::ValidateName(char* buffer, const char* context)
|
||||
{
|
||||
ChatLink::ValidateName(buffer, context);
|
||||
|
||||
bool res = false;
|
||||
for (uint8 i = 0; i < TOTAL_LOCALES; ++i)
|
||||
if (*_achievement->name[i] && strcmp(_achievement->name[i], buffer) == 0)
|
||||
{
|
||||
res = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!res)
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): linked achievement (id: %u) name wasn't found in any localization", context, _achievement->ID);
|
||||
return res;
|
||||
}
|
||||
|
||||
// |color|Htrade:spell_id:cur_value:max_value:player_guid:base64_data|h[name]|h|r
|
||||
// |cffffd000|Htrade:4037:1:150:1:6AAAAAAAAAAAAAAAAAAAAAAOAADAAAAAAAAAAAAAAAAIAAAAAAAAA|h[Engineering]|h|r
|
||||
bool TradeChatLink::Initialize(std::istringstream& iss)
|
||||
{
|
||||
if (_color != CHAT_LINK_COLOR_TRADE)
|
||||
return false;
|
||||
// Spell Id
|
||||
uint32 spellId = 0;
|
||||
if (!ReadUInt32(iss, spellId))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement entry", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Validate spell
|
||||
_spell = sSpellStore.LookupEntry(spellId);
|
||||
if (!_spell)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |trade command", iss.str().c_str(), spellId);
|
||||
return false;
|
||||
}
|
||||
// Check delimiter
|
||||
if (!CheckDelimiter(iss, DELIMITER, "trade"))
|
||||
return false;
|
||||
// Minimum talent level
|
||||
if (!ReadInt32(iss, _minSkillLevel))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading minimum talent level", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Check delimiter
|
||||
if (!CheckDelimiter(iss, DELIMITER, "trade"))
|
||||
return false;
|
||||
// Maximum talent level
|
||||
if (!ReadInt32(iss, _maxSkillLevel))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading maximum talent level", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Check delimiter
|
||||
if (!CheckDelimiter(iss, DELIMITER, "trade"))
|
||||
return false;
|
||||
// Something hexadecimal
|
||||
if (!ReadHex(iss, _guid, 0))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading achievement's owner guid", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Skip base64 encoded stuff
|
||||
_base64 = ReadSkip(iss, PIPE_CHAR);
|
||||
return true;
|
||||
}
|
||||
|
||||
// |color|Htalent:talent_id:rank|h[name]|h|r
|
||||
// |cff4e96f7|Htalent:2232:-1|h[Taste for Blood]|h|r
|
||||
bool TalentChatLink::Initialize(std::istringstream& iss)
|
||||
{
|
||||
if (_color != CHAT_LINK_COLOR_TALENT)
|
||||
return false;
|
||||
// Read talent entry
|
||||
if (!ReadUInt32(iss, _talentId))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading talent entry", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Validate talent
|
||||
TalentEntry const *talentInfo = sTalentStore.LookupEntry(_talentId);
|
||||
if (!talentInfo)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid talent id %u in |talent command", iss.str().c_str(), _talentId);
|
||||
return false;
|
||||
}
|
||||
// Validate talent's spell
|
||||
_spell = sSpellStore.LookupEntry(talentInfo->RankID[0]);
|
||||
if (!_spell)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |trade command", iss.str().c_str(), talentInfo->RankID[0]);
|
||||
return false;
|
||||
}
|
||||
// Delimiter
|
||||
if (!CheckDelimiter(iss, DELIMITER, "talent"))
|
||||
return false;
|
||||
// Rank
|
||||
if (!ReadInt32(iss, _rankId))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading talent rank", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r
|
||||
// |cffffd000|Henchant:3919|h[Engineering: Rough Dynamite]|h|r
|
||||
bool EnchantmentChatLink::Initialize(std::istringstream& iss)
|
||||
{
|
||||
if (_color != CHAT_LINK_COLOR_ENCHANT)
|
||||
return false;
|
||||
// Spell Id
|
||||
uint32 spellId = 0;
|
||||
if (!ReadUInt32(iss, spellId))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading enchantment spell entry", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Validate spell
|
||||
_spell = sSpellStore.LookupEntry(spellId);
|
||||
if (!_spell)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |enchant command", iss.str().c_str(), spellId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// |color|Hglyph:glyph_slot_id:glyph_prop_id|h[%s]|h|r
|
||||
// |cff66bbff|Hglyph:21:762|h[Glyph of Bladestorm]|h|r
|
||||
bool GlyphChatLink::Initialize(std::istringstream& iss)
|
||||
{
|
||||
if (_color != CHAT_LINK_COLOR_GLYPH)
|
||||
return false;
|
||||
// Slot
|
||||
if (!ReadUInt32(iss, _slotId))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading slot id", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Check delimiter
|
||||
if (!CheckDelimiter(iss, DELIMITER, "glyph"))
|
||||
return false;
|
||||
// Glyph Id
|
||||
uint32 glyphId = 0;
|
||||
if (!ReadUInt32(iss, glyphId))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly while reading glyph entry", iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
// Validate glyph
|
||||
_glyph = sGlyphPropertiesStore.LookupEntry(glyphId);
|
||||
if (!_glyph)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid glyph id %u in |glyph command", iss.str().c_str(), glyphId);
|
||||
return false;
|
||||
}
|
||||
// Validate glyph's spell
|
||||
_spell = sSpellStore.LookupEntry(_glyph->SpellId);
|
||||
if (!_spell)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid spell id %u in |glyph command", iss.str().c_str(), _glyph->SpellId);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
LinkExtractor::LinkExtractor(const char* msg) : _iss(msg) { }
|
||||
|
||||
LinkExtractor::~LinkExtractor()
|
||||
{
|
||||
for (Links::iterator itr = _links.begin(); itr != _links.end(); ++itr)
|
||||
delete (*itr);
|
||||
_links.clear();
|
||||
}
|
||||
|
||||
bool LinkExtractor::IsValidMessage()
|
||||
{
|
||||
const char validSequence[6] = "cHhhr";
|
||||
const char* validSequenceIterator = validSequence;
|
||||
|
||||
char buffer[256];
|
||||
|
||||
std::istringstream::pos_type startPos = 0;
|
||||
uint32 color = 0;
|
||||
|
||||
ChatLink* link = NULL;
|
||||
while (!_iss.eof())
|
||||
{
|
||||
if (validSequence == validSequenceIterator)
|
||||
{
|
||||
if (link)
|
||||
{
|
||||
_links.push_back(link);
|
||||
link = NULL;
|
||||
}
|
||||
_iss.ignore(255, PIPE_CHAR);
|
||||
startPos = _iss.tellg() - std::istringstream::pos_type(1);
|
||||
}
|
||||
else if (_iss.get() != PIPE_CHAR)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence aborted unexpectedly", _iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// pipe has always to be followed by at least one char
|
||||
if (_iss.peek() == '\0')
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): pipe followed by '\\0'", _iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// no further pipe commands
|
||||
if (_iss.eof())
|
||||
break;
|
||||
|
||||
char commandChar;
|
||||
_iss >> commandChar;
|
||||
|
||||
// | in normal messages is escaped by ||
|
||||
if (commandChar != PIPE_CHAR)
|
||||
{
|
||||
if (commandChar == *validSequenceIterator)
|
||||
{
|
||||
if (validSequenceIterator == validSequence+4)
|
||||
validSequenceIterator = validSequence;
|
||||
else
|
||||
++validSequenceIterator;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): invalid sequence, expected '%c' but got '%c'", _iss.str().c_str(), *validSequenceIterator, commandChar);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (validSequence != validSequenceIterator)
|
||||
{
|
||||
// no escaped pipes in sequences
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got escaped pipe in sequence", _iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (commandChar)
|
||||
{
|
||||
case 'c':
|
||||
if (!ReadHex(_iss, color, 8))
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): invalid hexadecimal number while reading color", _iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
// read chars up to colon = link type
|
||||
_iss.getline(buffer, 256, DELIMITER);
|
||||
if (_iss.eof())
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", _iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strcmp(buffer, "item") == 0)
|
||||
link = new ItemChatLink();
|
||||
else if (strcmp(buffer, "quest") == 0)
|
||||
link = new QuestChatLink();
|
||||
else if (strcmp(buffer, "trade") == 0)
|
||||
link = new TradeChatLink();
|
||||
else if (strcmp(buffer, "talent") == 0)
|
||||
link = new TalentChatLink();
|
||||
else if (strcmp(buffer, "spell") == 0)
|
||||
link = new SpellChatLink();
|
||||
else if (strcmp(buffer, "enchant") == 0)
|
||||
link = new EnchantmentChatLink();
|
||||
else if (strcmp(buffer, "achievement") == 0)
|
||||
link = new AchievementChatLink();
|
||||
else if (strcmp(buffer, "glyph") == 0)
|
||||
link = new GlyphChatLink();
|
||||
else
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): user sent unsupported link type '%s'", _iss.str().c_str(), buffer);
|
||||
return false;
|
||||
}
|
||||
link->SetColor(color);
|
||||
if (!link->Initialize(_iss))
|
||||
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 (_iss.get() != '[')
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): link caption doesn't start with '['", _iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
_iss.getline(buffer, 256, ']');
|
||||
if (_iss.eof())
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): sequence finished unexpectedly", _iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!link)
|
||||
return false;
|
||||
|
||||
if (!link->ValidateName(buffer, _iss.str().c_str()))
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (link)
|
||||
link->SetBounds(startPos, _iss.tellg());
|
||||
case '|':
|
||||
// no further payload
|
||||
break;
|
||||
default:
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): got invalid command |%c", _iss.str().c_str(), commandChar);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check if every opened sequence was also closed properly
|
||||
if (validSequence != validSequenceIterator)
|
||||
{
|
||||
LOG("ChatHandler::isValidChatMessage('%s'): EOF in active sequence", _iss.str().c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
169
src/server/game/Chat/ChatLink.h
Normal file
169
src/server/game/Chat/ChatLink.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2011 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/>.
|
||||
*/
|
||||
|
||||
#ifndef TRINITYCORE_CHATLINK_H
|
||||
#define TRINITYCORE_CHATLINK_H
|
||||
|
||||
#include "SharedDefines.h"
|
||||
#include <sstream>
|
||||
#include <list>
|
||||
|
||||
struct ItemLocale;
|
||||
struct ItemPrototype;
|
||||
struct ItemRandomSuffixEntry;
|
||||
struct ItemRandomPropertiesEntry;
|
||||
struct SpellEntry;
|
||||
struct AchievementEntry;
|
||||
struct GlyphPropertiesEntry;
|
||||
class Quest;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ChatLink - abstract base class for various links
|
||||
class ChatLink
|
||||
{
|
||||
public:
|
||||
ChatLink() : _color(0), _startPos(0), _endPos(0) { }
|
||||
void SetColor(uint32 color) { _color = color; }
|
||||
// This will allow to extract the whole link string from the message, if necessary.
|
||||
void SetBounds(std::istringstream::pos_type startPos, std::istringstream::pos_type endPos) { _startPos = startPos; _endPos = endPos; }
|
||||
|
||||
virtual bool Initialize(std::istringstream& iss) = 0;
|
||||
virtual bool ValidateName(char* buffer, const char* context) = 0;
|
||||
|
||||
protected:
|
||||
uint32 _color;
|
||||
std::string _name;
|
||||
std::istringstream::pos_type _startPos;
|
||||
std::istringstream::pos_type _endPos;
|
||||
};
|
||||
|
||||
// ItemChatLink - link to item
|
||||
class ItemChatLink : public ChatLink
|
||||
{
|
||||
public:
|
||||
ItemChatLink() : ChatLink(), _item(NULL), _suffix(NULL), _property(NULL) { }
|
||||
virtual bool Initialize(std::istringstream& iss);
|
||||
virtual bool ValidateName(char* buffer, const char* context);
|
||||
|
||||
protected:
|
||||
std::string _FormatName(uint8 index, ItemLocale const *locale, char* const* suffixStrings) const;
|
||||
|
||||
ItemPrototype const* _item;
|
||||
int32 _data[8];
|
||||
ItemRandomSuffixEntry const* _suffix;
|
||||
ItemRandomPropertiesEntry const* _property;
|
||||
};
|
||||
|
||||
// QuestChatLink - link to quest
|
||||
class QuestChatLink : public ChatLink
|
||||
{
|
||||
public:
|
||||
QuestChatLink() : ChatLink(), _quest(NULL), _questLevel(0) { }
|
||||
virtual bool Initialize(std::istringstream& iss);
|
||||
virtual bool ValidateName(char* buffer, const char* context);
|
||||
|
||||
protected:
|
||||
Quest const* _quest;
|
||||
int32 _questLevel;
|
||||
};
|
||||
|
||||
// SpellChatLink - link to quest
|
||||
class SpellChatLink : public ChatLink
|
||||
{
|
||||
public:
|
||||
SpellChatLink() : ChatLink(), _spell(NULL) { }
|
||||
virtual bool Initialize(std::istringstream& iss);
|
||||
virtual bool ValidateName(char* buffer, const char* context);
|
||||
|
||||
protected:
|
||||
SpellEntry const* _spell;
|
||||
};
|
||||
|
||||
// AchievementChatLink - link to quest
|
||||
class AchievementChatLink : public ChatLink
|
||||
{
|
||||
public:
|
||||
AchievementChatLink() : ChatLink(), _guid(0), _achievement(NULL) { }
|
||||
virtual bool Initialize(std::istringstream& iss);
|
||||
virtual bool ValidateName(char* buffer, const char* context);
|
||||
|
||||
protected:
|
||||
uint32 _guid;
|
||||
AchievementEntry const* _achievement;
|
||||
uint32 _data[8];
|
||||
};
|
||||
|
||||
// TradeChatLink - link to trade info
|
||||
class TradeChatLink : public SpellChatLink
|
||||
{
|
||||
public:
|
||||
TradeChatLink() : SpellChatLink(), _minSkillLevel(0), _maxSkillLevel(0), _guid(0) { }
|
||||
virtual bool Initialize(std::istringstream& iss);
|
||||
private:
|
||||
int32 _minSkillLevel;
|
||||
int32 _maxSkillLevel;
|
||||
uint32 _guid;
|
||||
std::string _base64;
|
||||
};
|
||||
|
||||
// TalentChatLink - link to talent
|
||||
class TalentChatLink : public SpellChatLink
|
||||
{
|
||||
public:
|
||||
TalentChatLink() : SpellChatLink(), _talentId(0), _rankId(0) { }
|
||||
virtual bool Initialize(std::istringstream& iss);
|
||||
|
||||
private:
|
||||
uint32 _talentId;
|
||||
int32 _rankId;
|
||||
};
|
||||
|
||||
// EnchantmentChatLink - link to enchantment
|
||||
class EnchantmentChatLink : public SpellChatLink
|
||||
{
|
||||
public:
|
||||
EnchantmentChatLink() : SpellChatLink() { }
|
||||
virtual bool Initialize(std::istringstream& iss);
|
||||
};
|
||||
|
||||
// GlyphChatLink - link to glyph
|
||||
class GlyphChatLink : public SpellChatLink
|
||||
{
|
||||
public:
|
||||
GlyphChatLink() : SpellChatLink(), _glyph(NULL), _slotId(0) { }
|
||||
virtual bool Initialize(std::istringstream& iss);
|
||||
private:
|
||||
uint32 _slotId;
|
||||
GlyphPropertiesEntry const* _glyph;
|
||||
};
|
||||
|
||||
class LinkExtractor
|
||||
{
|
||||
public:
|
||||
explicit LinkExtractor(const char* msg);
|
||||
~LinkExtractor();
|
||||
|
||||
bool IsValidMessage();
|
||||
|
||||
private:
|
||||
typedef std::list<ChatLink*> Links;
|
||||
Links _links;
|
||||
std::istringstream _iss;
|
||||
};
|
||||
|
||||
|
||||
#endif // TRINITYCORE_CHATLINK_H
|
||||
@@ -6910,6 +6910,9 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const *
|
||||
if (summon->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
|
||||
((Guardian*)summon)->InitStatsForLevel(level);
|
||||
|
||||
if (properties && properties->Category == SUMMON_CATEGORY_ALLY)
|
||||
summon->setFaction(caster->getFaction());
|
||||
|
||||
summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
|
||||
if (summon->HasUnitTypeMask(UNIT_MASK_MINION) && m_targets.HasDst())
|
||||
((Minion*)summon)->SetFollowAngle(m_caster->GetAngle(summon));
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
*/
|
||||
|
||||
#include "Common.h"
|
||||
#include "TicketMgr.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "SQLStorage.h"
|
||||
#include "SQLStorageImpl.h"
|
||||
#include "Log.h"
|
||||
#include "TicketMgr.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
#include "Chat.h"
|
||||
#include "World.h"
|
||||
|
||||
inline float GetAge(uint64 t) { return float(time(NULL) - t) / DAY; }
|
||||
|
||||
@@ -197,6 +200,8 @@ void GmTicket::TeleportTo(Player* player) const
|
||||
// Ticket manager
|
||||
TicketMgr::TicketMgr() : _lastTicketId(0), _lastSurveyId(0), _openTicketCount(0), _lastChange(time(NULL)), _status(true) { }
|
||||
|
||||
void TicketMgr::Initialize() { SetStatus(sWorld->getBoolConfig(CONFIG_ALLOW_TICKETS)); }
|
||||
|
||||
void TicketMgr::LoadTickets()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
|
||||
@@ -20,12 +20,10 @@
|
||||
|
||||
#include <string>
|
||||
#include <ace/Singleton.h>
|
||||
#include "Common.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "SQLStorage.h"
|
||||
#include "SQLStorageImpl.h"
|
||||
#include "Chat.h"
|
||||
#include "World.h"
|
||||
|
||||
#include "ObjectMgr.h"
|
||||
|
||||
class ChatHandler;
|
||||
|
||||
// from blizzard lua
|
||||
enum GMTicketSystemStatus
|
||||
@@ -211,7 +209,7 @@ public:
|
||||
uint32 GetOpenTicketCount() const { return _openTicketCount; }
|
||||
uint32 GetNextSurveyID() { return ++_lastSurveyId; }
|
||||
|
||||
void Initialize() { SetStatus(sWorld->getBoolConfig(CONFIG_ALLOW_TICKETS)); }
|
||||
void Initialize();
|
||||
|
||||
void ShowList(ChatHandler& handler, bool onlineOnly) const;
|
||||
void ShowClosedList(ChatHandler& handler) const;
|
||||
|
||||
Reference in New Issue
Block a user