Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .luacheckrc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ files["DragonToast/"] = {

read_globals = {
-- WoW API
"C_ChatInfo",
"IsInInstance", "UnitName", "UnitClass",
"UnitFactionGroup",
"GetItemInfo", "GetItemQualityColor", "GetItemCount", "C_Item", "C_Container",
Expand All @@ -60,6 +61,7 @@ files["DragonToast/"] = {
"ChatFrame_OpenChat", "IsShiftKeyDown",
"InCombatLockdown", "hooksecurefunc",
"InterfaceOptionsFrame_OpenToCategory", "Settings",
"geterrorhandler",

-- WoW Globals
"Enum", "RAID_CLASS_COLORS", "ITEM_QUALITY_COLORS",
Expand Down
24 changes: 24 additions & 0 deletions DragonToast/Core/ListenerUtils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,17 @@ local Utils = ns.ListenerUtils
-- Cache Lua globals
local tostring = tostring
local tonumber = tonumber
local type = type
local math_floor = math.floor
local string_format = string.format
local table_concat = table.concat
local table_insert = table.insert
local ipairs = ipairs
local next = next

-- Cache WoW API
local C_ChatInfo = C_ChatInfo

-- Pending item lookups: itemID (number) -> { buildFunc, filterFunc }
-- Multiple entries can share the same itemID (e.g. two loots of the same item in quick succession)
-- Use an array of entries per itemID to handle that correctly.
Expand Down Expand Up @@ -158,6 +162,26 @@ function Utils.FormatGold(copper)
return string_format("|T%d:0:0:0:0|t%s", Utils.GOLD_ICON, table_concat(parts, " "))
end

-------------------------------------------------------------------------------
-- IsIndexableChatMessage(msg, lineID)
--
-- Retail occasionally emits CHAT_MSG_* payloads as Blizzard "secret"
-- (censored) strings. Any index / match operation on them raises a
-- tainted-string error. Callers guard handlers with a string type check
-- and a C_ChatInfo.IsChatLineCensored probe before touching the message.
-- The C_ChatInfo namespace does not exist on TBC / MoP Classic, so it is
-- nil-checked here.
-------------------------------------------------------------------------------

function Utils.IsIndexableChatMessage(msg, lineID)
if type(msg) ~= "string" then return false end
if C_ChatInfo and C_ChatInfo.IsChatLineCensored and lineID
and C_ChatInfo.IsChatLineCensored(lineID) then
return false
end
return true
end

-------------------------------------------------------------------------------
-- RetryWithTimer(addon, buildFunc, filterFunc, retries)
-- Retries a build function up to MAX_RETRIES times at RETRY_INTERVAL seconds.
Expand Down
39 changes: 35 additions & 4 deletions DragonToast/Listeners/LootListener_Shared.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,18 @@ local GetItemInfo = GetItemInfo
local GetTime = GetTime
local UnitName = UnitName
local error = error
local geterrorhandler = geterrorhandler
local ipairs = ipairs
local pcall = pcall
local select = select
local tonumber = tonumber
local tostring = tostring
local type = type

-------------------------------------------------------------------------------
-- Chat message sanity guard - see Utils.IsIndexableChatMessage
-------------------------------------------------------------------------------

local PLAYER_UNIT = "player"

local owner
Expand Down Expand Up @@ -340,9 +348,21 @@ function ns.LootListenerShared.Create(config)
local moneyPatterns = BuildMoneyPatterns(config.moneyPatterns or DEFAULT_MONEY_PATTERNS)
local listener = {}

local function OnChatMsgLoot(_, msg)
local function OnChatMsgLoot(_, msg, ...)
-- CHAT_MSG_* payload position 11 is lineID; with (_, msg) consuming
-- event+text, lineID is the 10th element of the remaining varargs.
local lineID = select(10, ...)
-- Skip non-string or censored (tainted) payloads to avoid retail secret-string errors.
if not Utils.IsIndexableChatMessage(msg, lineID) then return end

local playerName = UnitName(PLAYER_UNIT) or UNKNOWN
local itemLink, quantity, looter, isSelf = ParseLootMessage(msg, lootCategories, playerName)
-- Parse under pcall; a tainted string can still slip through if Blizzard changes the
-- censoring contract, and a parser error must not break the event dispatcher.
local ok, itemLink, quantity, looter, isSelf = pcall(ParseLootMessage, msg, lootCategories, playerName)
if not ok then
geterrorhandler()("DragonToast: ParseLootMessage failed: " .. tostring(itemLink))
return
end
if not itemLink then return end

Utils.WaitForItem(
Expand All @@ -353,12 +373,23 @@ function ns.LootListenerShared.Create(config)
)
end

local function OnChatMsgMoney(_, msg)
local function OnChatMsgMoney(_, msg, ...)
-- CHAT_MSG_* payload position 11 is lineID; with (_, msg) consuming
-- event+text, lineID is the 10th element of the remaining varargs.
local lineID = select(10, ...)
-- Skip non-string or censored (tainted) payloads to avoid retail secret-string errors.
if not Utils.IsIndexableChatMessage(msg, lineID) then return end

local db = owner.db.profile
if not db.enabled or not db.filters.showGold then return end

local playerName = UnitName(PLAYER_UNIT) or UNKNOWN
local amount, looter, isSelf = ParseMoneyMessage(msg, moneyPatterns, playerName)
-- Defensive pcall: same tainted-string safety net as the loot handler.
local ok, amount, looter, isSelf = pcall(ParseMoneyMessage, msg, moneyPatterns, playerName)
if not ok then
geterrorhandler()("DragonToast: ParseMoneyMessage failed: " .. tostring(amount))
return
end
if not amount then return end

QueueMoneyToast(amount, looter, isSelf)
Expand Down
Loading