[10324] Implement new basic functions for chat command parsing.

* Now can be used as quotes any strings with symbols ' " [] around.
  For example can be used: .additem [Tourch] or .additem "Tourch".
  And in similar cases wher before [] or "" only canbe used in commands.

* New functions support propertly extraction shift-links as optional first args
* Also added more safe functions for extraction int32/uint32/float values.

For more wide use new functuons specialized extraction functions also need chnaged to same way work.
This is goal for future work at this part code.
This commit is contained in:
VladimirMangos 2010-08-06 06:14:18 +04:00
parent 373f607a44
commit edace1948e
7 changed files with 365 additions and 126 deletions

View file

@ -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<int32>::min() || valRaw > std::numeric_limits<int32>::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<uint32>::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;

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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');

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "10323"
#define REVISION_NR "10324"
#endif // __REVISION_NR_H__