/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* 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 .
*/
#include "AccountMgr.h"
#include "BattlenetAccountMgr.h"
#include "Chat.h"
#include "ChatCommand.h"
#include "CryptoRandom.h"
#include "DatabaseEnv.h"
#include "IPLocation.h"
#include "Language.h"
#include "Log.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "Util.h"
#include "WorldSession.h"
using namespace Trinity::ChatCommands;
class battlenet_account_commandscript : public CommandScript
{
public:
battlenet_account_commandscript() : CommandScript("battlenet_account_commandscript") { }
ChatCommandTable GetCommands() const override
{
static ChatCommandTable accountSetCommandTable =
{
{ "password", HandleAccountSetPasswordCommand,rbac::RBAC_PERM_COMMAND_BNET_ACCOUNT_SET_PASSWORD, Console::Yes },
};
static ChatCommandTable accountLockCommandTable =
{
{ "country", HandleAccountLockCountryCommand,rbac::RBAC_PERM_COMMAND_BNET_ACCOUNT_LOCK_COUNTRY, Console::Yes },
{ "ip", HandleAccountLockIpCommand, rbac::RBAC_PERM_COMMAND_BNET_ACCOUNT_LOCK_IP, Console::Yes },
};
static ChatCommandTable accountCommandTable =
{
{ "create", HandleAccountCreateCommand, rbac::RBAC_PERM_COMMAND_BNET_ACCOUNT_CREATE, Console::Yes },
{ "gameaccountcreate", HandleGameAccountCreateCommand, rbac::RBAC_PERM_COMMAND_BNET_ACCOUNT_CREATE_GAME, Console::Yes },
{ "lock", accountLockCommandTable },
{ "set", accountSetCommandTable },
{ "password", HandleAccountPasswordCommand, rbac::RBAC_PERM_COMMAND_BNET_ACCOUNT_PASSWORD, Console::No },
{ "link", HandleAccountLinkCommand, rbac::RBAC_PERM_COMMAND_BNET_ACCOUNT_LINK, Console::Yes },
{ "unlink", HandleAccountUnlinkCommand, rbac::RBAC_PERM_COMMAND_BNET_ACCOUNT_UNLINK, Console::Yes },
{ "listgameaccounts", HandleListGameAccountsCommand, rbac::RBAC_PERM_COMMAND_BNET_ACCOUNT_LIST_GAME_ACCOUTNS, Console::Yes }
};
static ChatCommandTable commandTable =
{
{ "bnetaccount", accountCommandTable },
};
return commandTable;
}
/// Create an account
static bool HandleAccountCreateCommand(ChatHandler* handler, std::string const& accountName, std::string const& password, Optional createGameAccount)
{
if (accountName.find('@') == std::string::npos)
{
handler->SendSysMessage(LANG_ACCOUNT_INVALID_BNET_NAME);
handler->SetSentErrorMessage(true);
return false;
}
std::string gameAccountName;
switch (Battlenet::AccountMgr::CreateBattlenetAccount(accountName, password, createGameAccount.value_or(true), &gameAccountName))
{
case AccountOpResult::AOR_OK:
{
if (createGameAccount != false)
handler->PSendSysMessage(LANG_ACCOUNT_CREATED_BNET_WITH_GAME, accountName.c_str(), gameAccountName.c_str());
else
handler->PSendSysMessage(LANG_ACCOUNT_CREATED_BNET, accountName.c_str());
if (handler->GetSession())
{
TC_LOG_INFO("entities.player.character", "Account: {} (IP: {}) Character:[{}] ({}) created Battle.net account {}{}{}",
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress(),
handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString(),
accountName, createGameAccount != false ? " with game account " : "", createGameAccount != false ? gameAccountName.c_str() : "");
}
break;
}
case AccountOpResult::AOR_NAME_TOO_LONG:
handler->SendSysMessage(LANG_ACCOUNT_NAME_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
case AccountOpResult::AOR_PASS_TOO_LONG:
handler->SendSysMessage(LANG_ACCOUNT_PASS_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
case AccountOpResult::AOR_NAME_ALREADY_EXIST:
handler->SendSysMessage(LANG_ACCOUNT_ALREADY_EXIST);
handler->SetSentErrorMessage(true);
return false;
default:
break;
}
return true;
}
// Sets country lock on own account
static bool HandleAccountLockCountryCommand(ChatHandler* handler, bool state)
{
if (state)
{
if (IpLocationRecord const* location = sIPLocation->GetLocationRecord(handler->GetSession()->GetRemoteAddress()))
{
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_ACCOUNT_LOCK_CONTRY);
stmt->setString(0, location->CountryCode);
stmt->setUInt32(1, handler->GetSession()->GetBattlenetAccountId());
LoginDatabase.Execute(stmt);
handler->PSendSysMessage(LANG_COMMAND_ACCLOCKLOCKED);
}
else
{
handler->PSendSysMessage("IP2Location] No information");
TC_LOG_DEBUG("server.bnetserver", "IP2Location] No information");
}
}
else
{
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_ACCOUNT_LOCK_CONTRY);
stmt->setString(0, "00"sv);
stmt->setUInt32(1, handler->GetSession()->GetBattlenetAccountId());
LoginDatabase.Execute(stmt);
handler->PSendSysMessage(LANG_COMMAND_ACCLOCKUNLOCKED);
}
return true;
}
// Sets ip lock on own account
static bool HandleAccountLockIpCommand(ChatHandler* handler, bool state)
{
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_ACCOUNT_LOCK);
if (state)
{
stmt->setBool(0, true); // locked
handler->PSendSysMessage(LANG_COMMAND_ACCLOCKLOCKED);
}
else
{
stmt->setBool(0, false); // unlocked
handler->PSendSysMessage(LANG_COMMAND_ACCLOCKUNLOCKED);
}
stmt->setUInt32(1, handler->GetSession()->GetBattlenetAccountId());
LoginDatabase.Execute(stmt);
return true;
}
static bool HandleAccountPasswordCommand(ChatHandler* handler, std::string const& oldPassword, std::string const& newPassword, std::string const& passwordConfirmation)
{
// We compare the old, saved password to the entered old password - no chance for the unauthorized.
if (!Battlenet::AccountMgr::CheckPassword(handler->GetSession()->GetBattlenetAccountId(), std::string(oldPassword)))
{
handler->SendSysMessage(LANG_COMMAND_WRONGOLDPASSWORD);
handler->SetSentErrorMessage(true);
TC_LOG_INFO("entities.player.character", "Battle.net account: {} (IP: {}) Character:[{}] ({}) Tried to change password, but the provided old password is wrong.",
handler->GetSession()->GetBattlenetAccountId(), handler->GetSession()->GetRemoteAddress(),
handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString());
return false;
}
// Making sure that newly entered password is correctly entered.
if (newPassword != passwordConfirmation)
{
handler->SendSysMessage(LANG_NEW_PASSWORDS_NOT_MATCH);
handler->SetSentErrorMessage(true);
return false;
}
// Changes password and prints result.
AccountOpResult result = Battlenet::AccountMgr::ChangePassword(handler->GetSession()->GetBattlenetAccountId(), std::string(newPassword));
switch (result)
{
case AccountOpResult::AOR_OK:
handler->SendSysMessage(LANG_COMMAND_PASSWORD);
TC_LOG_INFO("entities.player.character", "Battle.net account: {} (IP: {}) Character:[{}] ({}) Changed Password.",
handler->GetSession()->GetBattlenetAccountId(), handler->GetSession()->GetRemoteAddress(),
handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString());
break;
case AccountOpResult::AOR_PASS_TOO_LONG:
handler->SendSysMessage(LANG_PASSWORD_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
default:
handler->SendSysMessage(LANG_COMMAND_NOTCHANGEPASSWORD);
handler->SetSentErrorMessage(true);
return false;
}
return true;
}
/// Set password for account
static bool HandleAccountSetPasswordCommand(ChatHandler* handler, std::string accountName, std::string const& password, std::string const& passwordConfirmation)
{
if (!Utf8ToUpperOnlyLatin(accountName))
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
uint32 targetAccountId = Battlenet::AccountMgr::GetId(accountName);
if (!targetAccountId)
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
if (password != passwordConfirmation)
{
handler->SendSysMessage(LANG_NEW_PASSWORDS_NOT_MATCH);
handler->SetSentErrorMessage(true);
return false;
}
AccountOpResult result = Battlenet::AccountMgr::ChangePassword(targetAccountId, password);
switch (result)
{
case AccountOpResult::AOR_OK:
handler->SendSysMessage(LANG_COMMAND_PASSWORD);
break;
case AccountOpResult::AOR_NAME_NOT_EXIST:
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
case AccountOpResult::AOR_PASS_TOO_LONG:
handler->SendSysMessage(LANG_PASSWORD_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
default:
break;
}
return true;
}
static bool HandleAccountLinkCommand(ChatHandler* handler, std::string const& bnetAccountName, std::string const& gameAccountName)
{
switch (Battlenet::AccountMgr::LinkWithGameAccount(bnetAccountName, gameAccountName))
{
case AccountOpResult::AOR_OK:
handler->PSendSysMessage(LANG_ACCOUNT_BNET_LINKED, bnetAccountName.c_str(), gameAccountName.c_str());
break;
case AccountOpResult::AOR_NAME_NOT_EXIST:
handler->PSendSysMessage(LANG_ACCOUNT_OR_BNET_DOES_NOT_EXIST, bnetAccountName.c_str(), gameAccountName.c_str());
handler->SetSentErrorMessage(true);
break;
case AccountOpResult::AOR_ACCOUNT_BAD_LINK:
handler->PSendSysMessage(LANG_ACCOUNT_ALREADY_LINKED, gameAccountName.c_str());
handler->SetSentErrorMessage(true);
break;
default:
break;
}
return true;
}
static bool HandleAccountUnlinkCommand(ChatHandler* handler, std::string const& gameAccountName)
{
switch (Battlenet::AccountMgr::UnlinkGameAccount(gameAccountName))
{
case AccountOpResult::AOR_OK:
handler->PSendSysMessage(LANG_ACCOUNT_BNET_UNLINKED, gameAccountName.c_str());
break;
case AccountOpResult::AOR_NAME_NOT_EXIST:
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, gameAccountName.c_str());
handler->SetSentErrorMessage(true);
break;
case AccountOpResult::AOR_ACCOUNT_BAD_LINK:
handler->PSendSysMessage(LANG_ACCOUNT_BNET_NOT_LINKED, gameAccountName.c_str());
handler->SetSentErrorMessage(true);
break;
default:
break;
}
return true;
}
static bool HandleGameAccountCreateCommand(ChatHandler* handler, std::string const& bnetAccountName)
{
uint32 accountId = Battlenet::AccountMgr::GetId(bnetAccountName);
if (!accountId)
{
handler->PSendSysMessage(LANG_ACCOUNT_NOT_EXIST, bnetAccountName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
uint8 index = Battlenet::AccountMgr::GetMaxIndex(accountId) + 1;
std::string accountName = std::to_string(accountId) + '#' + std::to_string(uint32(index));
// Generate random hex string for password, these accounts must not be logged on with GRUNT
std::array randPassword = Trinity::Crypto::GetRandomBytes<8>();
switch (sAccountMgr->CreateAccount(accountName, ByteArrayToHexStr(randPassword), bnetAccountName, accountId, index))
{
case AccountOpResult::AOR_OK:
handler->PSendSysMessage(LANG_ACCOUNT_CREATED, accountName.c_str());
if (handler->GetSession())
{
TC_LOG_INFO("entities.player.character", "Account: {} (IP: {}) Character:[{}] ({}) created Account {} (Email: '{}')",
handler->GetSession()->GetAccountId(), handler->GetSession()->GetRemoteAddress(),
handler->GetSession()->GetPlayer()->GetName(), handler->GetSession()->GetPlayer()->GetGUID().ToString(),
accountName, bnetAccountName);
}
break;
case AccountOpResult::AOR_NAME_TOO_LONG:
handler->SendSysMessage(LANG_ACCOUNT_NAME_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
case AccountOpResult::AOR_PASS_TOO_LONG:
handler->SendSysMessage(LANG_ACCOUNT_PASS_TOO_LONG);
handler->SetSentErrorMessage(true);
return false;
case AccountOpResult::AOR_NAME_ALREADY_EXIST:
handler->SendSysMessage(LANG_ACCOUNT_ALREADY_EXIST);
handler->SetSentErrorMessage(true);
return false;
case AccountOpResult::AOR_DB_INTERNAL_ERROR:
handler->PSendSysMessage(LANG_ACCOUNT_NOT_CREATED_SQL_ERROR, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
default:
handler->PSendSysMessage(LANG_ACCOUNT_NOT_CREATED, accountName.c_str());
handler->SetSentErrorMessage(true);
return false;
}
return true;
}
static bool HandleListGameAccountsCommand(ChatHandler* handler, std::string const& battlenetAccountName)
{
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_GAME_ACCOUNT_LIST_SMALL);
stmt->setString(0, battlenetAccountName);
if (PreparedQueryResult accountList = LoginDatabase.Query(stmt))
{
auto formatDisplayName = [](char const* name) -> std::string
{
if (char const* hashPos = strchr(name, '#'))
return std::string("WoW") + ++hashPos;
else
return name;
};
handler->SendSysMessage("----------------------------------------------------");
handler->SendSysMessage(LANG_ACCOUNT_BNET_LIST_HEADER);
handler->SendSysMessage("----------------------------------------------------");
do
{
Field* fields = accountList->Fetch();
handler->PSendSysMessage("| %10u | %-16.16s | %-16.16s |", fields[0].GetUInt32(), fields[1].GetCString(), formatDisplayName(fields[1].GetCString()).c_str());
} while (accountList->NextRow());
handler->SendSysMessage("----------------------------------------------------");
}
else
handler->PSendSysMessage(LANG_ACCOUNT_BNET_LIST_NO_ACCOUNTS, battlenetAccountName);
return true;
}
};
void AddSC_battlenet_account_commandscript()
{
new battlenet_account_commandscript();
}