aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/server/game/Chat/Chat.cpp612
-rwxr-xr-xsrc/server/game/Chat/Chat.h2
-rw-r--r--src/server/game/Chat/ChatLink.cpp714
-rw-r--r--src/server/game/Chat/ChatLink.h169
-rwxr-xr-xsrc/server/game/Spells/SpellEffects.cpp3
-rwxr-xr-xsrc/server/game/Tickets/TicketMgr.cpp7
-rwxr-xr-xsrc/server/game/Tickets/TicketMgr.h12
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;