mirror of
https://github.com/OpenFusionProject/OpenFusion.git
synced 2025-01-22 08:30:06 +00:00
Validate name wheel names
This commit is contained in:
parent
d06c324aa3
commit
6b9ae4c325
@ -12,6 +12,8 @@ sandbox=true
|
||||
[login]
|
||||
# must be kept in sync with loginInfo.php
|
||||
port=23000
|
||||
# will all name wheel names be approved instantly?
|
||||
acceptallwheelnames=true
|
||||
# will all custom names be approved instantly?
|
||||
acceptallcustomnames=true
|
||||
# should attempts to log into non-existent accounts
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "TableData.hpp"
|
||||
|
||||
#include "servers/CNLoginServer.hpp"
|
||||
|
||||
#include "NPCManager.hpp"
|
||||
#include "Missions.hpp"
|
||||
#include "Items.hpp"
|
||||
@ -79,6 +81,25 @@ static void loadXDT(json& xdtData) {
|
||||
NPCManager::NPCData = xdtData["m_pNpcTable"]["m_pNpcData"];
|
||||
|
||||
try {
|
||||
// load name wheel names
|
||||
json firstNameData = xdtData["m_pNameTable"]["m_pFirstName"];
|
||||
for (json::iterator _name = firstNameData.begin(); _name != firstNameData.end(); _name++) {
|
||||
auto name = _name.value();
|
||||
LoginServer::WheelFirstNames.push_back(name["m_pstrNameString"]);
|
||||
}
|
||||
|
||||
json middleNameData = xdtData["m_pNameTable"]["m_pMiddleName"];
|
||||
for (json::iterator _name = middleNameData.begin(); _name != middleNameData.end(); _name++) {
|
||||
auto name = _name.value();
|
||||
LoginServer::WheelMiddleNames.push_back(name["m_pstrNameString"]);
|
||||
}
|
||||
|
||||
json lastNameData = xdtData["m_pNameTable"]["m_pLastName"];
|
||||
for (json::iterator _name = lastNameData.begin(); _name != lastNameData.end(); _name++) {
|
||||
auto name = _name.value();
|
||||
LoginServer::WheelLastNames.push_back(name["m_pstrNameString"]);
|
||||
}
|
||||
|
||||
// load warps
|
||||
json warpData = xdtData["m_pInstanceTable"]["m_pWarpData"];
|
||||
|
||||
|
@ -69,7 +69,7 @@ namespace Database {
|
||||
bool isNameFree(std::string firstName, std::string lastName);
|
||||
bool isSlotFree(int accountId, int slotNum);
|
||||
/// returns ID, 0 if something failed
|
||||
int createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID);
|
||||
int createCharacter(int slot, int accountId, const char* firstName, const char* lastName, int nameCheck);
|
||||
/// returns true if query succeeded
|
||||
bool finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character, int accountId);
|
||||
/// returns true if query succeeded
|
||||
@ -85,7 +85,7 @@ namespace Database {
|
||||
};
|
||||
void evaluateCustomName(int characterID, CustomName decision);
|
||||
/// returns true if query succeeded
|
||||
bool changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save, int accountId);
|
||||
bool changeName(int playerId, int accountId, const char* firstName, const char* lastName, int nameCheck);
|
||||
|
||||
// getting players
|
||||
void getPlayer(Player* plr, int id);
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include "db/internal.hpp"
|
||||
|
||||
#include "servers/CNLoginServer.hpp"
|
||||
|
||||
#include "bcrypt/BCrypt.hpp"
|
||||
|
||||
void Database::findAccount(Account* account, std::string login) {
|
||||
@ -291,7 +293,7 @@ bool Database::isSlotFree(int accountId, int slotNum) {
|
||||
return result;
|
||||
}
|
||||
|
||||
int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID) {
|
||||
int Database::createCharacter(int slot, int accountId, const char* firstName, const char* lastName, int nameCheck) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
|
||||
@ -304,22 +306,17 @@ int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
|
||||
)";
|
||||
sqlite3_stmt* stmt;
|
||||
std::string firstName = AUTOU16TOU8(save->szFirstName);
|
||||
std::string lastName = AUTOU16TOU8(save->szLastName);
|
||||
|
||||
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
sqlite3_bind_int(stmt, 1, AccountID);
|
||||
sqlite3_bind_int(stmt, 2, save->iSlotNum);
|
||||
sqlite3_bind_text(stmt, 3, firstName.c_str(), -1, NULL);
|
||||
sqlite3_bind_text(stmt, 4, lastName.c_str(), -1, NULL);
|
||||
sqlite3_bind_int(stmt, 1, accountId);
|
||||
sqlite3_bind_int(stmt, 2, slot);
|
||||
sqlite3_bind_text(stmt, 3, firstName, -1, NULL);
|
||||
sqlite3_bind_text(stmt, 4, lastName, -1, NULL);
|
||||
sqlite3_bind_int(stmt, 5, settings::SPAWN_X);
|
||||
sqlite3_bind_int(stmt, 6, settings::SPAWN_Y);
|
||||
sqlite3_bind_int(stmt, 7, settings::SPAWN_Z);
|
||||
sqlite3_bind_int(stmt, 8, settings::SPAWN_ANGLE);
|
||||
sqlite3_bind_int(stmt, 9, PC_MAXHEALTH(1));
|
||||
|
||||
// if FNCode isn't 0, it's a wheel name
|
||||
int nameCheck = (settings::APPROVEALLNAMES || save->iFNCode) ? 1 : 0;
|
||||
sqlite3_bind_int(stmt, 10, nameCheck);
|
||||
|
||||
// blobs
|
||||
@ -648,7 +645,7 @@ void Database::evaluateCustomName(int characterID, CustomName decision) {
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
bool Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save, int accountId) {
|
||||
bool Database::changeName(int playerId, int accountId, const char* firstName, const char* lastName, int nameCheck) {
|
||||
std::lock_guard<std::mutex> lock(dbCrit);
|
||||
|
||||
const char* sql = R"(
|
||||
@ -662,15 +659,10 @@ bool Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save, int accountId) {
|
||||
sqlite3_stmt* stmt;
|
||||
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
|
||||
|
||||
std::string firstName = AUTOU16TOU8(save->szFirstName);
|
||||
std::string lastName = AUTOU16TOU8(save->szLastName);
|
||||
|
||||
sqlite3_bind_text(stmt, 1, firstName.c_str(), -1, NULL);
|
||||
sqlite3_bind_text(stmt, 2, lastName.c_str(), -1, NULL);
|
||||
// if FNCode isn't 0, it's a wheel name
|
||||
int nameCheck = (settings::APPROVEALLNAMES || save->iFNCode) ? 1 : 0;
|
||||
sqlite3_bind_text(stmt, 1, firstName, -1, NULL);
|
||||
sqlite3_bind_text(stmt, 2, lastName, -1, NULL);
|
||||
sqlite3_bind_int(stmt, 3, nameCheck);
|
||||
sqlite3_bind_int(stmt, 4, save->iPCUID);
|
||||
sqlite3_bind_int(stmt, 4, playerId);
|
||||
sqlite3_bind_int(stmt, 5, accountId);
|
||||
|
||||
int rc = sqlite3_step(stmt);
|
||||
|
@ -13,6 +13,12 @@
|
||||
|
||||
std::map<CNSocket*, CNLoginData> CNLoginServer::loginSessions;
|
||||
|
||||
namespace LoginServer {
|
||||
std::vector<std::string> WheelFirstNames;
|
||||
std::vector<std::string> WheelMiddleNames;
|
||||
std::vector<std::string> WheelLastNames;
|
||||
}
|
||||
|
||||
CNLoginServer::CNLoginServer(uint16_t p) {
|
||||
serverType = "login";
|
||||
port = p;
|
||||
@ -286,11 +292,30 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
|
||||
INITSTRUCT(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC, resp);
|
||||
|
||||
int errorCode = 0;
|
||||
if (!CNLoginServer::isCharacterNameGood(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) {
|
||||
|
||||
std::string firstName = AUTOU16TOU8(save->szFirstName);
|
||||
std::string lastName = AUTOU16TOU8(save->szLastName);
|
||||
int nameCheck = 0;
|
||||
|
||||
// if FNCode isn't 0, it's a wheel name
|
||||
if (save->iFNCode != 0) {
|
||||
if (!CNLoginServer::isNameWheelNameGood(save->iFNCode, save->iMNCode, save->iLNCode, firstName, lastName)) {
|
||||
errorCode = 4;
|
||||
} else if (!Database::isNameFree(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) {
|
||||
} else {
|
||||
nameCheck = settings:: APPROVEWHEELNAMES ? 1 : 0;
|
||||
}
|
||||
} else {
|
||||
// custom name
|
||||
nameCheck = settings::APPROVECUSTOMNAMES ? 1 : 0;
|
||||
}
|
||||
|
||||
if (errorCode == 0) {
|
||||
if (!CNLoginServer::isCharacterNameGood(firstName, lastName)) {
|
||||
errorCode = 4;
|
||||
} else if (!Database::isNameFree(firstName, lastName)) {
|
||||
errorCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorCode != 0) {
|
||||
INITSTRUCT(sP_LS2CL_REP_CHECK_CHAR_NAME_FAIL, resp);
|
||||
@ -307,19 +332,16 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
|
||||
if (!Database::isSlotFree(loginSessions[sock].userID, save->iSlotNum))
|
||||
return invalidCharacter(sock);
|
||||
|
||||
resp.iPC_UID = Database::createCharacter(save, loginSessions[sock].userID);
|
||||
resp.iPC_UID = Database::createCharacter(save->iSlotNum, loginSessions[sock].userID, firstName.c_str(), lastName.c_str(), nameCheck);
|
||||
// if query somehow failed
|
||||
if (resp.iPC_UID == 0) {
|
||||
std::cout << "[WARN] Login Server: Database failed to create new character!" << std::endl;
|
||||
return invalidCharacter(sock);
|
||||
}
|
||||
|
||||
Player plr;
|
||||
Database::getPlayer(&plr, (int)resp.iPC_UID);
|
||||
|
||||
// fire name check event if needed
|
||||
if (plr.PCStyle.iNameCheck != 1) {
|
||||
std::string namereq = std::to_string(resp.iPC_UID) + " " + AUTOU16TOU8(save->szFirstName) + " " + AUTOU16TOU8(save->szLastName);
|
||||
if (nameCheck != 1) {
|
||||
std::string namereq = std::to_string(resp.iPC_UID) + " " + firstName + " " + lastName;
|
||||
Monitor::namereqs.push_back(namereq);
|
||||
}
|
||||
|
||||
@ -338,8 +360,8 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
|
||||
DEBUGLOG(
|
||||
std::cout << "Login Server: new character created" << std::endl;
|
||||
std::cout << "\tSlot: " << (int)save->iSlotNum << std::endl;
|
||||
std::cout << "\tName: " << AUTOU16TOU8(save->szFirstName) << " " << AUTOU16TOU8(save->szLastName);
|
||||
if (plr.PCStyle.iNameCheck != 1) std::cout << " (pending approval)";
|
||||
std::cout << "\tName: " << firstName << " " << lastName;
|
||||
if (nameCheck != 1) std::cout << " (pending approval)";
|
||||
std::cout << std::endl;
|
||||
)
|
||||
}
|
||||
@ -507,12 +529,31 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
|
||||
auto save = (sP_CL2LS_REQ_CHANGE_CHAR_NAME*)data->buf;
|
||||
|
||||
int errorCode = 0;
|
||||
if (!CNLoginServer::isCharacterNameGood(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) {
|
||||
|
||||
std::string firstName = AUTOU16TOU8(save->szFirstName);
|
||||
std::string lastName = AUTOU16TOU8(save->szLastName);
|
||||
int nameCheck = 0;
|
||||
|
||||
// if FNCode isn't 0, it's a wheel name
|
||||
if (save->iFNCode != 0) {
|
||||
if (!CNLoginServer::isNameWheelNameGood(save->iFNCode, save->iMNCode, save->iLNCode, firstName, lastName)) {
|
||||
errorCode = 4;
|
||||
} else {
|
||||
nameCheck = settings::APPROVEWHEELNAMES ? 1 : 0;
|
||||
}
|
||||
} else {
|
||||
// custom name
|
||||
nameCheck = settings::APPROVECUSTOMNAMES ? 1 : 0;
|
||||
}
|
||||
|
||||
if (errorCode == 0) {
|
||||
if (!CNLoginServer::isCharacterNameGood(firstName, lastName)) {
|
||||
errorCode = 4;
|
||||
}
|
||||
else if (!Database::isNameFree(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) {
|
||||
else if (!Database::isNameFree(firstName, lastName)) {
|
||||
errorCode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (errorCode != 0) {
|
||||
INITSTRUCT(sP_LS2CL_REP_CHECK_CHAR_NAME_FAIL, resp);
|
||||
@ -526,15 +567,12 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Database::changeName(save, loginSessions[sock].userID))
|
||||
if (!Database::changeName(save->iPCUID, loginSessions[sock].userID, firstName.c_str(), lastName.c_str(), nameCheck))
|
||||
return invalidCharacter(sock);
|
||||
|
||||
Player plr;
|
||||
Database::getPlayer(&plr, (int)save->iPCUID);
|
||||
|
||||
// fire name check event if needed
|
||||
if (plr.PCStyle.iNameCheck != 1) {
|
||||
std::string namereq = std::to_string(save->iPCUID) + " " + AUTOU16TOU8(save->szFirstName) + " " + AUTOU16TOU8(save->szLastName);
|
||||
if (nameCheck != 1) {
|
||||
std::string namereq = std::to_string(save->iPCUID) + " " + firstName + " " + lastName;
|
||||
Monitor::namereqs.push_back(namereq);
|
||||
}
|
||||
|
||||
@ -550,8 +588,8 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
|
||||
|
||||
DEBUGLOG(
|
||||
std::cout << "Login Server: Name change request for character [" << save->iPCUID << "]" << std::endl;
|
||||
std::cout << "\tNew name: " << AUTOU16TOU8(save->szFirstName) << " " << AUTOU16TOU8(save->szLastName);
|
||||
if (plr.PCStyle.iNameCheck != 1) std::cout << " (pending approval)";
|
||||
std::cout << "\tNew name: " << firstName << " " << lastName;
|
||||
if (nameCheck != 1) std::cout << " (pending approval)";
|
||||
std::cout << std::endl;
|
||||
)
|
||||
}
|
||||
@ -645,6 +683,42 @@ bool CNLoginServer::isPasswordCorrect(std::string actualPassword, std::string tr
|
||||
return BCrypt::validatePassword(tryPassword, actualPassword);
|
||||
}
|
||||
|
||||
bool CNLoginServer::isNameWheelNameGood(int fnCode, int mnCode, int lnCode, std::string& firstName, std::string& lastName) {
|
||||
if (fnCode >= LoginServer::WheelFirstNames.size()
|
||||
|| mnCode >= LoginServer::WheelMiddleNames.size()
|
||||
|| lnCode >= LoginServer::WheelLastNames.size()) {
|
||||
std::cout << "[WARN] Login Server: Invalid name codes received: " << fnCode << " " << mnCode << " " << lnCode << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// client sends 1 if not selected for these. they point to a single blank space. why.
|
||||
// just change them to 0, which points to an empty string; keeps the code much cleaner
|
||||
if (mnCode == 1) mnCode = 0;
|
||||
if (lnCode == 1) lnCode = 0;
|
||||
|
||||
std::string firstNameFromWheel = LoginServer::WheelFirstNames[fnCode];
|
||||
|
||||
std::string middleNamePart = LoginServer::WheelMiddleNames[mnCode];
|
||||
std::string lastNamePart = LoginServer::WheelLastNames[lnCode];
|
||||
if (mnCode != 0 && middleNamePart[middleNamePart.size() - 1] != ' ') {
|
||||
// If there's a middle name, we need to lowercase the last name
|
||||
std::transform(lastNamePart.begin(), lastNamePart.end(), lastNamePart.begin(), ::tolower);
|
||||
}
|
||||
std::string lastNameFromWheel = middleNamePart + lastNamePart;
|
||||
|
||||
if (firstNameFromWheel.empty() || lastNameFromWheel.empty()) {
|
||||
std::cout << "[WARN] Login Server: Invalid wheel name combo: " << fnCode << " " << mnCode << " " << lnCode << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (firstName != firstNameFromWheel || lastName != lastNameFromWheel) {
|
||||
std::cout << "[WARN] Login Server: Name wheel mismatch. Expected " << firstNameFromWheel << " " << lastNameFromWheel << ", got " << firstName << " " << lastName << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNLoginServer::isCharacterNameGood(std::string Firstname, std::string Lastname) {
|
||||
//Allow alphanumeric and dot characters in names(disallows dot and space characters at the beginning of a name)
|
||||
std::regex firstnamecheck(R"(((?! )(?!\.)[a-zA-Z0-9]*\.{0,1}(?!\.+ +)[a-zA-Z0-9]* {0,1}(?! +))*$)");
|
||||
|
@ -7,6 +7,12 @@
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace LoginServer {
|
||||
extern std::vector<std::string> WheelFirstNames;
|
||||
extern std::vector<std::string> WheelMiddleNames;
|
||||
extern std::vector<std::string> WheelLastNames;
|
||||
}
|
||||
|
||||
struct CNLoginData {
|
||||
int userID;
|
||||
time_t lastHeartbeat;
|
||||
@ -51,6 +57,7 @@ private:
|
||||
static bool isPasswordGood(std::string& password);
|
||||
static bool isPasswordCorrect(std::string actualPassword, std::string tryPassword);
|
||||
static bool isAccountInUse(int accountId);
|
||||
static bool isNameWheelNameGood(int fnCode, int mnCode, int lnCode, std::string& firstName, std::string& lastName);
|
||||
static bool isCharacterNameGood(std::string Firstname, std::string Lastname);
|
||||
static bool isAuthMethodAllowed(AuthMethod authMethod);
|
||||
static bool checkUsername(CNSocket* sock, std::string& username);
|
||||
|
@ -12,7 +12,8 @@ bool settings::SANDBOX = true;
|
||||
std::string settings::SANDBOXEXTRAPATH = "";
|
||||
|
||||
int settings::LOGINPORT = 23000;
|
||||
bool settings::APPROVEALLNAMES = true;
|
||||
bool settings::APPROVEWHEELNAMES = true;
|
||||
bool settings::APPROVECUSTOMNAMES = true;
|
||||
bool settings::AUTOCREATEACCOUNTS = true;
|
||||
std::string settings::AUTHMETHODS = "password";
|
||||
int settings::DBSAVEINTERVAL = 240;
|
||||
@ -89,7 +90,8 @@ void settings::init() {
|
||||
SANDBOX = reader.GetBoolean("", "sandbox", SANDBOX);
|
||||
SANDBOXEXTRAPATH = reader.Get("", "sandboxextrapath", SANDBOXEXTRAPATH);
|
||||
LOGINPORT = reader.GetInteger("login", "port", LOGINPORT);
|
||||
APPROVEALLNAMES = reader.GetBoolean("login", "acceptallcustomnames", APPROVEALLNAMES);
|
||||
APPROVEWHEELNAMES = reader.GetBoolean("login", "acceptallwheelnames", APPROVEWHEELNAMES);
|
||||
APPROVECUSTOMNAMES = reader.GetBoolean("login", "acceptallcustomnames", APPROVECUSTOMNAMES);
|
||||
AUTOCREATEACCOUNTS = reader.GetBoolean("login", "autocreateaccounts", AUTOCREATEACCOUNTS);
|
||||
AUTHMETHODS = reader.Get("login", "authmethods", AUTHMETHODS);
|
||||
DBSAVEINTERVAL = reader.GetInteger("login", "dbsaveinterval", DBSAVEINTERVAL);
|
||||
|
@ -9,7 +9,8 @@ namespace settings {
|
||||
extern bool SANDBOX;
|
||||
extern std::string SANDBOXEXTRAPATH;
|
||||
extern int LOGINPORT;
|
||||
extern bool APPROVEALLNAMES;
|
||||
extern bool APPROVEWHEELNAMES;
|
||||
extern bool APPROVECUSTOMNAMES;
|
||||
extern bool AUTOCREATEACCOUNTS;
|
||||
extern std::string AUTHMETHODS;
|
||||
extern int DBSAVEINTERVAL;
|
||||
|
Loading…
Reference in New Issue
Block a user