diff --git a/src/CNLoginServer.cpp b/src/CNLoginServer.cpp index 53ccbdb..7947479 100644 --- a/src/CNLoginServer.cpp +++ b/src/CNLoginServer.cpp @@ -118,8 +118,20 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) { userPassword.erase(userPassword.find("\n"), 1); // check regex - if (!CNLoginServer::isLoginDataGood(userLogin, userPassword)) + if (!CNLoginServer::isLoginDataGood(userLogin, userPassword)) { + // send a custom error message + INITSTRUCT(sP_FE2CL_GM_REP_PC_ANNOUNCE, msg); + std::string text = "Invalid login or password\n"; + text += "Login has to be 4 - 32 characters long and can't contain special characters other than dash and underscore\n"; + text += "Password has to be 8 - 32 characters long"; + U8toU16(text, msg.szAnnounceMsg, sizeof(msg.szAnnounceMsg)); + msg.iDuringTime = 15; + sock->sendPacket((void*)&msg, P_FE2CL_GM_REP_PC_ANNOUNCE, sizeof(sP_FE2CL_GM_REP_PC_ANNOUNCE)); + + // we still have to send login fail to prevent softlock return loginFail(LoginError::LOGIN_ERROR, userLogin, sock); + } + Database::Account findUser = {}; Database::findAccount(&findUser, userLogin); @@ -133,7 +145,25 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) { // is the account banned if (findUser.BannedUntil > getTimestamp()) - return loginFail(LoginError::LOGIN_ERROR, userLogin, sock); + { + // send a custom error message + INITSTRUCT(sP_FE2CL_GM_REP_PC_ANNOUNCE, msg); + + // ceiling devision + int64_t remainingDays = (findUser.BannedUntil-getTimestamp()) / 86400 + ((findUser.BannedUntil - getTimestamp()) % 86400 != 0); + + std::string text = "Your account has been banned by game master\nReason: "; + text += findUser.BanReason; + text += "\nBan expires in " + std::to_string(remainingDays) + " day"; + if (remainingDays > 1) + text += "s"; + + U8toU16(text, msg.szAnnounceMsg, sizeof(msg.szAnnounceMsg)); + msg.iDuringTime = 99999999; + sock->sendPacket((void*)&msg, P_FE2CL_GM_REP_PC_ANNOUNCE, sizeof(sP_FE2CL_GM_REP_PC_ANNOUNCE)); + // don't send fail packet to softlock sucker's client + return; + } /* * calling this here to timestamp login attempt, @@ -180,7 +210,10 @@ void CNLoginServer::login(CNSocket* sock, CNPacketData* data) { sock->sendPacket((void*)&*it, P_LS2CL_REP_CHAR_INFO, sizeof(sP_LS2CL_REP_CHAR_INFO)); DEBUGLOG( - std::cout << "Login Server: Loaded " << (int)resp.iCharCount << " characters" << std::endl; + std::string message = "Login Server: Loaded " + std::to_string(resp.iCharCount) + "character"; + if ((int)resp.iCharCount > 1) + message += "s"; + std::cout << message << std::endl; ) } diff --git a/src/Database.cpp b/src/Database.cpp index 85bebc0..8966aaa 100644 --- a/src/Database.cpp +++ b/src/Database.cpp @@ -128,9 +128,28 @@ void Database::checkMetaTable() { sqlite3_finalize(stmt); if (dbVersion != DATABASE_VERSION) { - // we should be handling migrations here in the future - std::cout << "[FATAL] DB Version doesn't match Server Build" << std::endl; - exit(1); + // migrations + if (dbVersion == 1) { + // Version 1 - > 2 + sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL); + + int rc = sqlite3_exec(db, "ALTER TABLE Accounts ADD BanReason TEXT", NULL, NULL, NULL); + if (rc != SQLITE_OK) { + std::cout << "[FATAL] Failed to apply Database migration" << std::endl; + exit(1); + } + rc = sqlite3_exec(db, "UPDATE Meta SET Value = 2 WHERE Key = 'DatabaseVersion';", NULL, NULL, NULL); + if (rc != SQLITE_OK) { + std::cout << "[FATAL] Failed to apply Database migration" << std::endl; + exit(1); + } + + sqlite3_exec(db, "COMMIT;", NULL, NULL, NULL); + } + else { + std::cout << "[FATAL] Invalid DB Version" << std::endl; + exit(1); + } } } @@ -203,6 +222,7 @@ void Database::createTables() { LastLogin INTEGER DEFAULT (strftime('%s', 'now')) NOT NULL, BannedUntil INTEGER DEFAULT 0 NOT NULL, BannedSince INTEGER DEFAULT 0 NOT NULL, + BanReason TEXT, PRIMARY KEY(AccountID AUTOINCREMENT) ); @@ -370,7 +390,7 @@ void Database::findAccount(Account* account, std::string login) { std::lock_guard lock(dbCrit); const char* sql = R"( - SELECT AccountID, Password, Selected, BannedUntil + SELECT AccountID, Password, Selected, BannedUntil, BanReason FROM Accounts WHERE Login = ? LIMIT 1; @@ -386,6 +406,7 @@ void Database::findAccount(Account* account, std::string login) { account->Password = std::string(reinterpret_cast(sqlite3_column_text(stmt, 1))); account->Selected = sqlite3_column_int(stmt, 2); account->BannedUntil = sqlite3_column_int64(stmt, 3); + account->BanReason = reinterpret_cast(sqlite3_column_text(stmt, 4)); } sqlite3_finalize(stmt); } diff --git a/src/Database.hpp b/src/Database.hpp index 8564aee..6ed8cca 100644 --- a/src/Database.hpp +++ b/src/Database.hpp @@ -4,7 +4,7 @@ #include #include -#define DATABASE_VERSION 1 +#define DATABASE_VERSION 2 namespace Database { @@ -13,6 +13,7 @@ namespace Database { std::string Password; int Selected; time_t BannedUntil; + std::string BanReason; }; struct EmailData { int PlayerId;