/* * This file is part of the AzerothCore 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 "Common.h" #include "DatabaseEnv.h" #include "ObjectAccessor.h" #include "Player.h" #include "SRP6.h" #include "ScriptMgr.h" #include "Util.h" #include "WorldSession.h" namespace AccountMgr { AccountOpResult CreateAccount(std::string username, std::string password, std::string email /*= ""*/) { if (utf8length(username) > MAX_ACCOUNT_STR) return AOR_NAME_TOO_LONG; // username's too long if (utf8length(password) > MAX_PASS_STR) return AOR_PASS_TOO_LONG; // password's too long if (utf8length(email) > MAX_EMAIL_STR) return AOR_EMAIL_TOO_LONG; // email is too long Utf8ToUpperOnlyLatin(username); Utf8ToUpperOnlyLatin(password); Utf8ToUpperOnlyLatin(email); if (GetId(username)) return AOR_NAME_ALREADY_EXIST; // username does already exist LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT); stmt->SetData(0, username); auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(username, password); stmt->SetData(1, salt); stmt->SetData(2, verifier); stmt->SetData(3, uint8(sWorld->getIntConfig(CONFIG_EXPANSION))); stmt->SetData(4, email); stmt->SetData(5, email); LoginDatabase.Execute(stmt); stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS_INIT); LoginDatabase.Execute(stmt); return AOR_OK; // everything's fine } AccountOpResult ChangeEmail(uint32 accountId, std::string newEmail) { std::string username; if (!GetName(accountId, username)) { sScriptMgr->OnFailedEmailChange(accountId); return AOR_NAME_NOT_EXIST; // account doesn't exist } if (utf8length(newEmail) > MAX_EMAIL_STR) { sScriptMgr->OnFailedEmailChange(accountId); return AOR_EMAIL_TOO_LONG; // email's too long } Utf8ToUpperOnlyLatin(newEmail); LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EMAIL); stmt->SetData(0, newEmail); stmt->SetData(1, accountId); LoginDatabase.Execute(stmt); sScriptMgr->OnEmailChange(accountId); return AOR_OK; } AccountOpResult DeleteAccount(uint32 accountId) { // Check if accounts exists LoginDatabasePreparedStatement* loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); loginStmt->SetData(0, accountId); PreparedQueryResult result = LoginDatabase.Query(loginStmt); if (!result) return AOR_NAME_NOT_EXIST; sScriptMgr->OnBeforeAccountDelete(accountId); // Obtain accounts characters CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID); stmt->SetData(0, accountId); result = CharacterDatabase.Query(stmt); if (result) { do { ObjectGuid guid = ObjectGuid::Create((*result)[0].Get()); // Kick if player is online if (Player* p = ObjectAccessor::FindPlayer(guid)) { WorldSession* s = p->GetSession(); s->KickPlayer("Delete account"); // mark session to remove at next session list update s->LogoutPlayer(false); // logout player without waiting next session list update } Player::DeleteFromDB(guid.GetCounter(), accountId, false, true); // no need to update realm characters } while (result->NextRow()); } // table realm specific but common for all characters of account for realm stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_TUTORIALS); stmt->SetData(0, accountId); CharacterDatabase.Execute(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_DATA); stmt->SetData(0, accountId); CharacterDatabase.Execute(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_BAN); stmt->SetData(0, accountId); CharacterDatabase.Execute(stmt); LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT); loginStmt->SetData(0, accountId); trans->Append(loginStmt); loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS); loginStmt->SetData(0, accountId); trans->Append(loginStmt); loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS); loginStmt->SetData(0, accountId); trans->Append(loginStmt); loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_BANNED); loginStmt->SetData(0, accountId); trans->Append(loginStmt); loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_MUTED); loginStmt->SetData(0, accountId); trans->Append(loginStmt); LoginDatabase.CommitTransaction(trans); return AOR_OK; } AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword) { // Check if accounts exists LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); stmt->SetData(0, accountId); PreparedQueryResult result = LoginDatabase.Query(stmt); if (!result) return AOR_NAME_NOT_EXIST; if (utf8length(newUsername) > MAX_ACCOUNT_STR) return AOR_NAME_TOO_LONG; if (utf8length(newPassword) > MAX_PASS_STR) return AOR_PASS_TOO_LONG; // password's too long Utf8ToUpperOnlyLatin(newUsername); Utf8ToUpperOnlyLatin(newPassword); stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_USERNAME); stmt->SetData(0, newUsername); stmt->SetData(1, accountId); LoginDatabase.Execute(stmt); auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(newUsername, newPassword); stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON); stmt->SetData(0, salt); stmt->SetData(1, verifier); stmt->SetData(2, accountId); LoginDatabase.Execute(stmt); return AOR_OK; } AccountOpResult ChangePassword(uint32 accountId, std::string newPassword) { std::string username; if (!GetName(accountId, username)) { sScriptMgr->OnFailedPasswordChange(accountId); return AOR_NAME_NOT_EXIST; // account doesn't exist } if (utf8length(newPassword) > MAX_PASS_STR) { sScriptMgr->OnFailedEmailChange(accountId); return AOR_PASS_TOO_LONG; // password's too long } Utf8ToUpperOnlyLatin(username); Utf8ToUpperOnlyLatin(newPassword); auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(username, newPassword); LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON); stmt->SetData(0, salt); stmt->SetData(1, verifier); stmt->SetData(2, accountId); LoginDatabase.Execute(stmt); sScriptMgr->OnPasswordChange(accountId); return AOR_OK; } uint32 GetId(std::string const& username) { LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME); stmt->SetData(0, username); PreparedQueryResult result = LoginDatabase.Query(stmt); return (result) ? (*result)[0].Get() : 0; } uint32 GetSecurity(uint32 accountId) { LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL); stmt->SetData(0, accountId); PreparedQueryResult result = LoginDatabase.Query(stmt); return (result) ? (*result)[0].Get() : uint32(SEC_PLAYER); } uint32 GetSecurity(uint32 accountId, int32 realmId) { LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); stmt->SetData(0, accountId); stmt->SetData(1, realmId); PreparedQueryResult result = LoginDatabase.Query(stmt); return (result) ? (*result)[0].Get() : uint32(SEC_PLAYER); } bool GetName(uint32 accountId, std::string& name) { LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID); stmt->SetData(0, accountId); PreparedQueryResult result = LoginDatabase.Query(stmt); if (result) { name = (*result)[0].Get(); return true; } return false; } bool CheckPassword(uint32 accountId, std::string password) { std::string username; if (!GetName(accountId, username)) return false; Utf8ToUpperOnlyLatin(username); Utf8ToUpperOnlyLatin(password); LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD); stmt->SetData(0, accountId); if (PreparedQueryResult result = LoginDatabase.Query(stmt)) { Acore::Crypto::SRP6::Salt salt = (*result)[0].Get(); Acore::Crypto::SRP6::Verifier verifier = (*result)[1].Get(); if (Acore::Crypto::SRP6::CheckLogin(username, password, salt, verifier)) return true; } return false; } uint32 GetCharactersCount(uint32 accountId) { // check character count CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); stmt->SetData(0, accountId); PreparedQueryResult result = CharacterDatabase.Query(stmt); return (result) ? (*result)[0].Get() : 0; } bool IsPlayerAccount(uint32 gmlevel) { return gmlevel == SEC_PLAYER; } bool IsGMAccount(uint32 gmlevel) { return gmlevel >= SEC_GAMEMASTER; } bool IsAdminAccount(uint32 gmlevel) { return gmlevel >= SEC_ADMINISTRATOR && gmlevel <= SEC_CONSOLE; } bool IsConsoleAccount(uint32 gmlevel) { return gmlevel == SEC_CONSOLE; } } // Namespace AccountMgr