diff options
-rwxr-xr-x | src/server/game/Chat/Chat.cpp | 612 | ||||
-rwxr-xr-x | src/server/game/Chat/Chat.h | 2 | ||||
-rw-r--r-- | src/server/game/Chat/ChatLink.cpp | 714 | ||||
-rw-r--r-- | src/server/game/Chat/ChatLink.h | 169 | ||||
-rwxr-xr-x | src/server/game/Spells/SpellEffects.cpp | 3 | ||||
-rwxr-xr-x | src/server/game/Tickets/TicketMgr.cpp | 7 | ||||
-rwxr-xr-x | src/server/game/Tickets/TicketMgr.h | 12 |
7 files changed, 903 insertions, 616 deletions
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 686697b4aa8..b394ba94211 100755 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -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) diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h index 442a741366f..bef14f17c8e 100755 --- a/src/server/game/Chat/Chat.h +++ b/src/server/game/Chat/Chat.h @@ -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); diff --git a/src/server/game/Chat/ChatLink.cpp b/src/server/game/Chat/ChatLink.cpp new file mode 100644 index 00000000000..95ac685548f --- /dev/null +++ b/src/server/game/Chat/ChatLink.cpp @@ -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; +} diff --git a/src/server/game/Chat/ChatLink.h b/src/server/game/Chat/ChatLink.h new file mode 100644 index 00000000000..0e716fdd6cf --- /dev/null +++ b/src/server/game/Chat/ChatLink.h @@ -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
\ No newline at end of file diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index ff52e9247df..ae06e6f7505 100755 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -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)); diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp index 81ebc794669..1789c713a89 100755 --- a/src/server/game/Tickets/TicketMgr.cpp +++ b/src/server/game/Tickets/TicketMgr.cpp @@ -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(); diff --git a/src/server/game/Tickets/TicketMgr.h b/src/server/game/Tickets/TicketMgr.h index 9f222727510..c2e8b8d620b 100755 --- a/src/server/game/Tickets/TicketMgr.h +++ b/src/server/game/Tickets/TicketMgr.h @@ -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; |