diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index c5f8c5b9a..0c9524a4a 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -52,7 +52,7 @@ // |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|Hplayer:name|h[name]|h|r - client, in some messages, at click copy only name instead link, so no way generate it in client string send to server // |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 @@ -2005,6 +2005,281 @@ Creature* ChatHandler::getSelectedCreature() return m_session->GetPlayer()->GetMap()->GetCreatureOrPetOrVehicle(m_session->GetPlayer()->GetSelection()); } +/** + * Function skip all whitespaces in args string + * + * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces) + * allowed NULL string pointer stored in *args + */ +void ChatHandler::SkipWhiteSpaces(char** args) +{ + if(!*args) + return; + + while(isWhiteSpace(**args)) + ++(*args); +} + +/** + * Function extract to val arg signed integer value or fail + * + * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces) + * @param val return extracted value if function success, in fail case original value unmodified + * @return true if value extraction successful + */ +bool ChatHandler::ExtractInt32(char** args, int32& val) +{ + if (!*args || !**args) + return false; + + char* tail = *args; + + long valRaw = strtol(*args, &tail, 10); + + if (tail != *args && isWhiteSpace(*tail)) + *(tail++) = '\0'; + else if (tail && *tail) // some not whitespace symbol + return false; // args not modified and can be re-parsed + + if (valRaw < std::numeric_limits::min() || valRaw > std::numeric_limits::max()) + return false; + + // value successfully extracted + val = int32(valRaw); + *args = tail; + return true; +} + +/** + * Function extract to val arg unsigned integer value or fail + * + * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces) + * @param val return extracted value if function success, in fail case original value unmodified + * @return true if value extraction successful + */ +bool ChatHandler::ExtractUInt32(char** args, uint32& val) +{ + if (!*args || !**args) + return false; + + char* tail = *args; + + unsigned long valRaw = strtoul(*args, &tail, 10); + + if (tail != *args && isWhiteSpace(*tail)) + *(tail++) = '\0'; + else if (tail && *tail) // some not whitespace symbol + return false; // args not modified and can be re-parsed + + if (valRaw > std::numeric_limits::max()) + return false; + + // value successfully extracted + val = uint32(valRaw); + *args = tail; + return true; +} + +/** + * Function extract to val arg float value or fail + * + * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces) + * @param val return extracted value if function success, in fail case original value unmodified + * @return true if value extraction successful + */ +bool ChatHandler::ExtractFloat(char** args, float& val) +{ + if (!*args || !**args) + return false; + + char* tail = *args; + + double valRaw = strtod(*args, &tail); + + if (tail != *args && isWhiteSpace(*tail)) + *(tail++) = '\0'; + else if (tail && *tail) // some not whitespace symbol + return false; // args not modified and can be re-parsed + + // value successfully extracted + val = float(valRaw); + *args = tail; + return true; +} + +/** + * Function extract name-like string (from non-numeric or special symbol until whitespace) + * + * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces) + * @return name-like string without whitespaces, or NULL if args empty or not appropriate content. + */ +char* ChatHandler::ExtractLiteralArg(char** args) +{ + if (!*args || !**args) + return NULL; + + if ((*args)[0] == '[' || (*args)[0] == '\'' || (*args)[0] == '"' || (*args)[0] == '|') + return NULL; + + char* name = strtok(*args, " "); + + *args = strtok(NULL, ""); + + return name; +} + +/** + * Function extract quote-like string (any characters guarded by some special character, in our cases ['") + * + * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces) + * @return quote-like string, or NULL if args empty or not appropriate content. + */ +char* ChatHandler::ExtractQuotedArg( char** args ) +{ + if (!*args || !**args) + return NULL; + + if (**args != '\'' && **args != '"' && **args != '[') + return NULL; + + char guard[2] = " "; // guard[1] == '\0' + + guard[0] = (*args)[0]; + + if (guard[0] == '[') + guard[0] = ']'; + + char* str = strtok((*args)+1, guard); // skip start guard symbol + + *args = strtok(NULL, ""); + + SkipWhiteSpaces(args); + + return str; +} + +/** + * Function extract shift-link-like string (any characters guarded by | and |h|r with some additional internal structure check) + * + * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces) + * @return shift-link-like string, or NULL if args empty or not appropriate content. + */ +char* ChatHandler::ExtractLinkArg( char** args ) +{ + if (!*args || !**args) + return NULL; + + if (**args != '|') + return NULL; + + // |color|Hkey:data|h[name]|h|r + + char* head = *args; + char* tail = (*args)+1; // skip | + + while (*tail && *tail != '|') // skip color part + ++tail; + + if (!*tail) + return NULL; + + // |Hkey:data|h[name]|h|r + + ++tail; // skip | + + if (*tail != 'H') + return NULL; + + while (*tail && (*tail != '|' || *(tail+1) != 'h')) // skip key/data part + ++tail; + + if (!*tail) + return NULL; + + tail += 2; + + // [name]|h|r + if (!*tail || *tail != '[') + return NULL; + + while (*tail && (*tail != ']' || *(tail+1) != '|')) // skip name part + ++tail; + + tail += 2; + + // h|r + if (!*tail || *tail != 'h' || *(tail+1) != '|') + return NULL; + + tail += 2; + + // r + if (!*tail || *tail != 'r' || *(tail+1) && !isWhiteSpace(*(tail+1))) + return NULL; + + ++tail; + + if (*tail) + { + *(tail++) = '\0'; + } + + *args = tail; + + SkipWhiteSpaces(args); + + return head; +} + +/** + * Function extract nmae/number/quote/shift-link-like string + * + * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces) + * @return extaractd arg string, or NULL if args empty or not appropriate content. + */ +char* ChatHandler::ExtractArg( char** args ) +{ + if (!*args || !**args) + return NULL; + + switch (**args) + { + case '|' : + return ExtractLinkArg(args); + case '\'' : case '"' : case '[' : + return ExtractQuotedArg(args); + default: + { + char* name = strtok(*args, " "); + + *args = strtok(NULL, ""); + + return name; + } + } +} + +/** + * Function extract name/quote/number/shift-link-like string, and return it if args have more non-whitespace data + * + * @param args variable pointer to non parsed args string, updated at function call to new position (with skipped white spaces) + * if args gave only single arg then args still pointing to this arg (unmodified pointer) + * @return extracted string, or NULL if args empty or not appropriate content or have single arg totally. + */ +char* ChatHandler::ExtractOptArg(char** args) +{ + char* arg = ExtractArg(args); + + // have more data + if (*args && **args) + return arg; + + // optional name not found + *args = arg; + + return NULL; +} + char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** something1) { // skip empty @@ -2584,40 +2859,6 @@ bool ChatHandler::extractPlayerTarget(char* args, Player** player, uint64* playe return true; } -void ChatHandler::extractOptFirstArg(char* args, char** arg1, char** arg2) -{ - char* p1 = strtok(args, " "); - char* p2 = strtok(NULL, " "); - - if(!p2) - { - p2 = p1; - p1 = NULL; - } - - if(arg1) - *arg1 = p1; - - if(arg2) - *arg2 = p2; -} - -char* ChatHandler::extractQuotedArg( char* args ) -{ - if(!*args) - return NULL; - - if(*args=='"') - return strtok(args+1, "\""); - else - { - char* space = strtok(args, "\""); - if(!space) - return NULL; - return strtok(NULL, "\""); - } -} - uint32 ChatHandler::extractAccountId(char* args, std::string* accountName /*= NULL*/, Player** targetIfNullArg /*= NULL*/) { uint32 account_id = 0; diff --git a/src/game/Chat.h b/src/game/Chat.h index 4725c62c7..591d35c03 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -554,13 +554,20 @@ class ChatHandler Creature* getSelectedCreature(); Unit* getSelectedUnit(); + // extraction different type params from args string, all functions update (char** args) to first unparsed tail symbol at return + void SkipWhiteSpaces(char** args); + bool ExtractInt32(char** args, int32& val); + bool ExtractUInt32(char** args, uint32& val); + bool ExtractFloat(char** args, float& val); + char* ExtractLiteralArg(char** args); // any literal strings (until whitespace and not started from "['|) + char* ExtractQuotedArg(char** args); // string with " or [] or ' around + char* ExtractLinkArg(char** args); // shift-link like arg + char* ExtractArg(char** args); // any name/number/quote/shift-link strings + char* ExtractOptArg(char** args); // extract name/number/quote/shift-link arg only if more data in args for parse + char* extractKeyFromLink(char* text, char const* linkType, char** something1 = NULL); char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = NULL); - // if args have single value then it return in arg2 and arg1 == NULL - void extractOptFirstArg(char* args, char** arg1, char** arg2); - char* extractQuotedArg(char* args); - uint32 extractSpellIdFromLink(char* text); uint64 extractGuidFromLink(char* text); GameTele const* extractGameTeleFromLink(char* text); diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp index 7bbcdf283..c3828de07 100644 --- a/src/game/Level1.cpp +++ b/src/game/Level1.cpp @@ -1891,19 +1891,15 @@ bool ChatHandler::HandleSendMailCommand(char* args) if (!extractPlayerTarget(args, &target, &target_guid, &target_name)) return false; - char* tail1 = strtok(NULL, ""); - if(!tail1) + char* tail = strtok(NULL, ""); + if(!tail) return false; - char* msgSubject = extractQuotedArg(tail1); + char* msgSubject = ExtractQuotedArg(&tail); if (!msgSubject) return false; - char* tail2 = strtok(NULL, ""); - if(!tail2) - return false; - - char* msgText = extractQuotedArg(tail2); + char* msgText = ExtractQuotedArg(&tail); if (!msgText) return false; @@ -1925,11 +1921,7 @@ bool ChatHandler::HandleSendMailCommand(char* args) // teleport player to given game_tele.entry bool ChatHandler::HandleTeleNameCommand(char* args) { - char* nameStr; - char* teleStr; - extractOptFirstArg(args, &nameStr, &teleStr); - if (!teleStr) - return false; + char* nameStr = ExtractOptArg(&args); Player* target; uint64 target_guid; @@ -1938,7 +1930,7 @@ bool ChatHandler::HandleTeleNameCommand(char* args) return false; // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r - GameTele const* tele = extractGameTeleFromLink(teleStr); + GameTele const* tele = extractGameTeleFromLink(args); if (!tele) { SendSysMessage(LANG_COMMAND_TELE_NOTFOUND); diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp index 40fc01aeb..0831d73a8 100644 --- a/src/game/Level2.cpp +++ b/src/game/Level2.cpp @@ -53,11 +53,7 @@ static uint32 ReputationRankStrIndex[MAX_REPUTATION_RANK] = //mute player for some times bool ChatHandler::HandleMuteCommand(char* args) { - char* nameStr; - char* delayStr; - extractOptFirstArg(args, &nameStr, &delayStr); - if (!delayStr) - return false; + char* nameStr = ExtractOptArg(&args); Player* target; uint64 target_guid; @@ -65,6 +61,10 @@ bool ChatHandler::HandleMuteCommand(char* args) if (!extractPlayerTarget(nameStr, &target, &target_guid, &target_name)) return false; + uint32 notspeaktime; + if (!ExtractUInt32(&args, notspeaktime)) + return false; + uint32 account_id = target ? target->GetSession()->GetAccountId() : sObjectMgr.GetPlayerAccountIdByGUID(target_guid); // find only player from same account if any @@ -74,8 +74,6 @@ bool ChatHandler::HandleMuteCommand(char* args) target = session->GetPlayer(); } - uint32 notspeaktime = (uint32) atoi(delayStr); - // must have strong lesser security level if (HasLowerSecurity(target, target_guid, true)) return false; diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 9ec63e856..3d878c79c 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -918,14 +918,11 @@ bool ChatHandler::HandleLoadScriptsCommand(char* args) bool ChatHandler::HandleAccountSetGmLevelCommand(char* args) { - char* arg1; - char* arg2; - - extractOptFirstArg(args, &arg1, &arg2); + char* accountStr = ExtractOptArg(&args); std::string targetAccountName; Player* targetPlayer = NULL; - uint32 targetAccountId = extractAccountId(arg1, &targetAccountName, &targetPlayer); + uint32 targetAccountId = extractAccountId(accountStr, &targetAccountName, &targetPlayer); if (!targetAccountId) return false; @@ -933,7 +930,10 @@ bool ChatHandler::HandleAccountSetGmLevelCommand(char* args) if (GetAccountId() == targetAccountId) return false; - int32 gm = (int32)atoi(arg2); + int32 gm; + if (!ExtractInt32(&args, gm)) + return false; + if ( gm < SEC_PLAYER || gm > SEC_ADMINISTRATOR ) { SendSysMessage(LANG_BAD_VALUE); @@ -3279,11 +3279,11 @@ bool ChatHandler::HandleGuildCreateCommand(char* args) if(!extractPlayerTarget(*args!='"' ? args : NULL, &target)) return false; - char* tailStr = *args!='"' ? strtok(NULL, "") : args; - if(!tailStr) + char* tail = *args!='"' ? strtok(NULL, "") : args; + if(!tail) return false; - char* guildStr = extractQuotedArg(tailStr); + char* guildStr = ExtractQuotedArg(&tail); if(!guildStr) return false; @@ -3318,11 +3318,11 @@ bool ChatHandler::HandleGuildInviteCommand(char *args) if(!extractPlayerTarget(*args!='"' ? args : NULL, NULL, &target_guid)) return false; - char* tailStr = *args!='"' ? strtok(NULL, "") : args; - if(!tailStr) + char* tail = *args!='"' ? strtok(NULL, "") : args; + if(!tail) return false; - char* guildStr = extractQuotedArg(tailStr); + char* guildStr = ExtractQuotedArg(&tail); if(!guildStr) return false; @@ -3359,11 +3359,7 @@ bool ChatHandler::HandleGuildUninviteCommand(char *args) bool ChatHandler::HandleGuildRankCommand(char *args) { - char* nameStr; - char* rankStr; - extractOptFirstArg(args, &nameStr, &rankStr); - if(!rankStr) - return false; + char* nameStr = ExtractOptArg(&args); Player* target; uint64 target_guid; @@ -3379,7 +3375,10 @@ bool ChatHandler::HandleGuildRankCommand(char *args) if (!targetGuild) return false; - uint32 newrank = uint32 (atoi (rankStr)); + uint32 newrank; + if (!ExtractUInt32(&args, newrank)) + return false; + if (newrank > targetGuild->GetLowestRank ()) return false; @@ -3392,7 +3391,7 @@ bool ChatHandler::HandleGuildDeleteCommand(char* args) if (!*args) return false; - char* guildStr = extractQuotedArg(args); + char* guildStr = ExtractQuotedArg(&args); if(!guildStr) return false; @@ -4033,17 +4032,23 @@ void ChatHandler::HandleCharacterLevel(Player* player, uint64 player_guid, uint3 bool ChatHandler::HandleCharacterLevelCommand(char* args) { - char* nameStr; - char* levelStr; - extractOptFirstArg(args, &nameStr, &levelStr); - if (!levelStr) - return false; + char* nameStr = ExtractOptArg(&args); + int32 newlevel; + bool nolevel = false; // exception opt second arg: .character level $name - if (isalpha(levelStr[0])) + if (!ExtractInt32(&args, newlevel)) { - nameStr = levelStr; - levelStr = NULL; // current level will used + if (!nameStr) + { + nameStr = ExtractArg(&args); + if (!nameStr) + return false; + + nolevel = true; + } + else + return false; } Player* target; @@ -4053,7 +4058,8 @@ bool ChatHandler::HandleCharacterLevelCommand(char* args) return false; int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromDB(target_guid); - int32 newlevel = levelStr ? atoi(levelStr) : oldlevel; + if (nolevel) + newlevel = oldlevel; if (newlevel < 1) return false; // invalid level @@ -4061,7 +4067,7 @@ bool ChatHandler::HandleCharacterLevelCommand(char* args) if (newlevel > STRONG_MAX_LEVEL) // hardcoded maximum level newlevel = STRONG_MAX_LEVEL; - HandleCharacterLevel(target,target_guid,oldlevel,newlevel); + HandleCharacterLevel(target, target_guid, oldlevel, newlevel); if (!m_session || m_session->GetPlayer() != target) // including player==NULL { @@ -4074,15 +4080,21 @@ bool ChatHandler::HandleCharacterLevelCommand(char* args) bool ChatHandler::HandleLevelUpCommand(char* args) { - char* nameStr; - char* levelStr; - extractOptFirstArg(args, &nameStr, &levelStr); + int32 addlevel = 1; + char* nameStr = NULL; - // exception opt second arg: .character level $name - if (levelStr && isalpha(levelStr[0])) + if (*args) { - nameStr = levelStr; - levelStr = NULL; // current level will used + nameStr = ExtractOptArg(&args); + + // exception opt second arg: .levelup $name + if (!ExtractInt32(&args, addlevel)) + { + if (!nameStr) + nameStr = ExtractArg(&args); + else + return false; + } } Player* target; @@ -4092,7 +4104,6 @@ bool ChatHandler::HandleLevelUpCommand(char* args) return false; int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromDB(target_guid); - int32 addlevel = levelStr ? atoi(levelStr) : 1; int32 newlevel = oldlevel + addlevel; if (newlevel < 1) @@ -6295,13 +6306,10 @@ bool ChatHandler::HandleAccountCharactersCommand(char* args) bool ChatHandler::HandleAccountSetAddonCommand(char* args) { ///- Get the command line arguments - char* arg1; - char* arg2; - - extractOptFirstArg(args, &arg1, &arg2); + char* accountStr = ExtractOptArg(&args); std::string account_name; - uint32 account_id = extractAccountId(arg1, &account_name); + uint32 account_id = extractAccountId(accountStr, &account_name); if (!account_id ) return false; @@ -6311,8 +6319,8 @@ bool ChatHandler::HandleAccountSetAddonCommand(char* args) HasLowerSecurityAccount (NULL,account_id,true)) return false; - int lev=atoi(arg2); //get int anyway (0 if error) - if (lev < 0) + uint32 lev; + if (!ExtractUInt32(&args, lev)) return false; // No SQL injection @@ -6331,19 +6339,15 @@ bool ChatHandler::HandleSendItemsCommand(char* args) if (!extractPlayerTarget(args, &receiver, &receiver_guid, &receiver_name)) return false; - char* tail1 = strtok(NULL, ""); - if (!tail1) + char* tail = strtok(NULL, ""); + if (!tail) return false; - char* msgSubject = extractQuotedArg(tail1); + char* msgSubject = ExtractQuotedArg(&tail); if (!msgSubject) return false; - char* tail2 = strtok(NULL, ""); - if(!tail2) - return false; - - char* msgText = extractQuotedArg(tail2); + char* msgText = ExtractQuotedArg(&tail); if (!msgText) return false; @@ -6356,9 +6360,6 @@ bool ChatHandler::HandleSendItemsCommand(char* args) typedef std::list< ItemPair > ItemPairs; ItemPairs items; - // get all tail string - char* tail = strtok(NULL, ""); - // get from tail next item str while(char* itemStr = strtok(tail, " ")) { @@ -6438,24 +6439,19 @@ bool ChatHandler::HandleSendMoneyCommand(char* args) if (!extractPlayerTarget(args, &receiver, &receiver_guid, &receiver_name)) return false; - char* tail1 = strtok(NULL, ""); - if (!tail1) + char* tail = strtok(NULL, ""); + if (!tail) return false; - char* msgSubject = extractQuotedArg(tail1); + char* msgSubject = ExtractQuotedArg(&tail); if (!msgSubject) return false; - char* tail2 = strtok(NULL, ""); - if (!tail2) - return false; - - char* msgText = extractQuotedArg(tail2); + char* msgText = ExtractQuotedArg(&tail); if (!msgText) return false; - char* money_str = strtok(NULL, ""); - int32 money = money_str ? atoi(money_str) : 0; + int32 money = tail ? atoi(tail) : 0; if (money <= 0) return false; diff --git a/src/shared/Util.h b/src/shared/Util.h index e19c273eb..fa8e8ae49 100644 --- a/src/shared/Util.h +++ b/src/shared/Util.h @@ -176,6 +176,11 @@ inline bool isEastAsianCharacter(wchar_t wchar) return false; } +inline bool isWhiteSpace(char c) +{ + return ::isspace(int(c)) != 0; +} + inline bool isNumeric(wchar_t wchar) { return (wchar >= L'0' && wchar <=L'9'); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index c08be58e5..678f79e3e 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "10323" + #define REVISION_NR "10324" #endif // __REVISION_NR_H__