Skip to content

Commit 6b9ae4c

Browse files
Validate name wheel names
1 parent d06c324 commit 6b9ae4c

File tree

8 files changed

+147
-48
lines changed

8 files changed

+147
-48
lines changed

config.ini

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ sandbox=true
1212
[login]
1313
# must be kept in sync with loginInfo.php
1414
port=23000
15+
# will all name wheel names be approved instantly?
16+
acceptallwheelnames=true
1517
# will all custom names be approved instantly?
1618
acceptallcustomnames=true
1719
# should attempts to log into non-existent accounts

src/TableData.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "TableData.hpp"
22

3+
#include "servers/CNLoginServer.hpp"
4+
35
#include "NPCManager.hpp"
46
#include "Missions.hpp"
57
#include "Items.hpp"
@@ -79,6 +81,25 @@ static void loadXDT(json& xdtData) {
7981
NPCManager::NPCData = xdtData["m_pNpcTable"]["m_pNpcData"];
8082

8183
try {
84+
// load name wheel names
85+
json firstNameData = xdtData["m_pNameTable"]["m_pFirstName"];
86+
for (json::iterator _name = firstNameData.begin(); _name != firstNameData.end(); _name++) {
87+
auto name = _name.value();
88+
LoginServer::WheelFirstNames.push_back(name["m_pstrNameString"]);
89+
}
90+
91+
json middleNameData = xdtData["m_pNameTable"]["m_pMiddleName"];
92+
for (json::iterator _name = middleNameData.begin(); _name != middleNameData.end(); _name++) {
93+
auto name = _name.value();
94+
LoginServer::WheelMiddleNames.push_back(name["m_pstrNameString"]);
95+
}
96+
97+
json lastNameData = xdtData["m_pNameTable"]["m_pLastName"];
98+
for (json::iterator _name = lastNameData.begin(); _name != lastNameData.end(); _name++) {
99+
auto name = _name.value();
100+
LoginServer::WheelLastNames.push_back(name["m_pstrNameString"]);
101+
}
102+
82103
// load warps
83104
json warpData = xdtData["m_pInstanceTable"]["m_pWarpData"];
84105

src/db/Database.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ namespace Database {
6969
bool isNameFree(std::string firstName, std::string lastName);
7070
bool isSlotFree(int accountId, int slotNum);
7171
/// returns ID, 0 if something failed
72-
int createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID);
72+
int createCharacter(int slot, int accountId, const char* firstName, const char* lastName, int nameCheck);
7373
/// returns true if query succeeded
7474
bool finishCharacter(sP_CL2LS_REQ_CHAR_CREATE* character, int accountId);
7575
/// returns true if query succeeded
@@ -85,7 +85,7 @@ namespace Database {
8585
};
8686
void evaluateCustomName(int characterID, CustomName decision);
8787
/// returns true if query succeeded
88-
bool changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save, int accountId);
88+
bool changeName(int playerId, int accountId, const char* firstName, const char* lastName, int nameCheck);
8989

9090
// getting players
9191
void getPlayer(Player* plr, int id);

src/db/login.cpp

+11-19
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include "db/internal.hpp"
44

5+
#include "servers/CNLoginServer.hpp"
6+
57
#include "bcrypt/BCrypt.hpp"
68

79
void Database::findAccount(Account* account, std::string login) {
@@ -291,7 +293,7 @@ bool Database::isSlotFree(int accountId, int slotNum) {
291293
return result;
292294
}
293295

294-
int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID) {
296+
int Database::createCharacter(int slot, int accountId, const char* firstName, const char* lastName, int nameCheck) {
295297
std::lock_guard<std::mutex> lock(dbCrit);
296298

297299
sqlite3_exec(db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
@@ -304,22 +306,17 @@ int Database::createCharacter(sP_CL2LS_REQ_SAVE_CHAR_NAME* save, int AccountID)
304306
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
305307
)";
306308
sqlite3_stmt* stmt;
307-
std::string firstName = AUTOU16TOU8(save->szFirstName);
308-
std::string lastName = AUTOU16TOU8(save->szLastName);
309309

310310
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
311-
sqlite3_bind_int(stmt, 1, AccountID);
312-
sqlite3_bind_int(stmt, 2, save->iSlotNum);
313-
sqlite3_bind_text(stmt, 3, firstName.c_str(), -1, NULL);
314-
sqlite3_bind_text(stmt, 4, lastName.c_str(), -1, NULL);
311+
sqlite3_bind_int(stmt, 1, accountId);
312+
sqlite3_bind_int(stmt, 2, slot);
313+
sqlite3_bind_text(stmt, 3, firstName, -1, NULL);
314+
sqlite3_bind_text(stmt, 4, lastName, -1, NULL);
315315
sqlite3_bind_int(stmt, 5, settings::SPAWN_X);
316316
sqlite3_bind_int(stmt, 6, settings::SPAWN_Y);
317317
sqlite3_bind_int(stmt, 7, settings::SPAWN_Z);
318318
sqlite3_bind_int(stmt, 8, settings::SPAWN_ANGLE);
319319
sqlite3_bind_int(stmt, 9, PC_MAXHEALTH(1));
320-
321-
// if FNCode isn't 0, it's a wheel name
322-
int nameCheck = (settings::APPROVEALLNAMES || save->iFNCode) ? 1 : 0;
323320
sqlite3_bind_int(stmt, 10, nameCheck);
324321

325322
// blobs
@@ -648,7 +645,7 @@ void Database::evaluateCustomName(int characterID, CustomName decision) {
648645
sqlite3_finalize(stmt);
649646
}
650647

651-
bool Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save, int accountId) {
648+
bool Database::changeName(int playerId, int accountId, const char* firstName, const char* lastName, int nameCheck) {
652649
std::lock_guard<std::mutex> lock(dbCrit);
653650

654651
const char* sql = R"(
@@ -662,15 +659,10 @@ bool Database::changeName(sP_CL2LS_REQ_CHANGE_CHAR_NAME* save, int accountId) {
662659
sqlite3_stmt* stmt;
663660
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
664661

665-
std::string firstName = AUTOU16TOU8(save->szFirstName);
666-
std::string lastName = AUTOU16TOU8(save->szLastName);
667-
668-
sqlite3_bind_text(stmt, 1, firstName.c_str(), -1, NULL);
669-
sqlite3_bind_text(stmt, 2, lastName.c_str(), -1, NULL);
670-
// if FNCode isn't 0, it's a wheel name
671-
int nameCheck = (settings::APPROVEALLNAMES || save->iFNCode) ? 1 : 0;
662+
sqlite3_bind_text(stmt, 1, firstName, -1, NULL);
663+
sqlite3_bind_text(stmt, 2, lastName, -1, NULL);
672664
sqlite3_bind_int(stmt, 3, nameCheck);
673-
sqlite3_bind_int(stmt, 4, save->iPCUID);
665+
sqlite3_bind_int(stmt, 4, playerId);
674666
sqlite3_bind_int(stmt, 5, accountId);
675667

676668
int rc = sqlite3_step(stmt);

src/servers/CNLoginServer.cpp

+98-24
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313

1414
std::map<CNSocket*, CNLoginData> CNLoginServer::loginSessions;
1515

16+
namespace LoginServer {
17+
std::vector<std::string> WheelFirstNames;
18+
std::vector<std::string> WheelMiddleNames;
19+
std::vector<std::string> WheelLastNames;
20+
}
21+
1622
CNLoginServer::CNLoginServer(uint16_t p) {
1723
serverType = "login";
1824
port = p;
@@ -286,10 +292,29 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
286292
INITSTRUCT(sP_LS2CL_REP_SAVE_CHAR_NAME_SUCC, resp);
287293

288294
int errorCode = 0;
289-
if (!CNLoginServer::isCharacterNameGood(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) {
290-
errorCode = 4;
291-
} else if (!Database::isNameFree(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) {
292-
errorCode = 1;
295+
296+
std::string firstName = AUTOU16TOU8(save->szFirstName);
297+
std::string lastName = AUTOU16TOU8(save->szLastName);
298+
int nameCheck = 0;
299+
300+
// if FNCode isn't 0, it's a wheel name
301+
if (save->iFNCode != 0) {
302+
if (!CNLoginServer::isNameWheelNameGood(save->iFNCode, save->iMNCode, save->iLNCode, firstName, lastName)) {
303+
errorCode = 4;
304+
} else {
305+
nameCheck = settings:: APPROVEWHEELNAMES ? 1 : 0;
306+
}
307+
} else {
308+
// custom name
309+
nameCheck = settings::APPROVECUSTOMNAMES ? 1 : 0;
310+
}
311+
312+
if (errorCode == 0) {
313+
if (!CNLoginServer::isCharacterNameGood(firstName, lastName)) {
314+
errorCode = 4;
315+
} else if (!Database::isNameFree(firstName, lastName)) {
316+
errorCode = 1;
317+
}
293318
}
294319

295320
if (errorCode != 0) {
@@ -307,19 +332,16 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
307332
if (!Database::isSlotFree(loginSessions[sock].userID, save->iSlotNum))
308333
return invalidCharacter(sock);
309334

310-
resp.iPC_UID = Database::createCharacter(save, loginSessions[sock].userID);
335+
resp.iPC_UID = Database::createCharacter(save->iSlotNum, loginSessions[sock].userID, firstName.c_str(), lastName.c_str(), nameCheck);
311336
// if query somehow failed
312337
if (resp.iPC_UID == 0) {
313338
std::cout << "[WARN] Login Server: Database failed to create new character!" << std::endl;
314339
return invalidCharacter(sock);
315340
}
316341

317-
Player plr;
318-
Database::getPlayer(&plr, (int)resp.iPC_UID);
319-
320342
// fire name check event if needed
321-
if (plr.PCStyle.iNameCheck != 1) {
322-
std::string namereq = std::to_string(resp.iPC_UID) + " " + AUTOU16TOU8(save->szFirstName) + " " + AUTOU16TOU8(save->szLastName);
343+
if (nameCheck != 1) {
344+
std::string namereq = std::to_string(resp.iPC_UID) + " " + firstName + " " + lastName;
323345
Monitor::namereqs.push_back(namereq);
324346
}
325347

@@ -338,8 +360,8 @@ void CNLoginServer::nameSave(CNSocket* sock, CNPacketData* data) {
338360
DEBUGLOG(
339361
std::cout << "Login Server: new character created" << std::endl;
340362
std::cout << "\tSlot: " << (int)save->iSlotNum << std::endl;
341-
std::cout << "\tName: " << AUTOU16TOU8(save->szFirstName) << " " << AUTOU16TOU8(save->szLastName);
342-
if (plr.PCStyle.iNameCheck != 1) std::cout << " (pending approval)";
363+
std::cout << "\tName: " << firstName << " " << lastName;
364+
if (nameCheck != 1) std::cout << " (pending approval)";
343365
std::cout << std::endl;
344366
)
345367
}
@@ -507,11 +529,30 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
507529
auto save = (sP_CL2LS_REQ_CHANGE_CHAR_NAME*)data->buf;
508530

509531
int errorCode = 0;
510-
if (!CNLoginServer::isCharacterNameGood(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) {
511-
errorCode = 4;
532+
533+
std::string firstName = AUTOU16TOU8(save->szFirstName);
534+
std::string lastName = AUTOU16TOU8(save->szLastName);
535+
int nameCheck = 0;
536+
537+
// if FNCode isn't 0, it's a wheel name
538+
if (save->iFNCode != 0) {
539+
if (!CNLoginServer::isNameWheelNameGood(save->iFNCode, save->iMNCode, save->iLNCode, firstName, lastName)) {
540+
errorCode = 4;
541+
} else {
542+
nameCheck = settings::APPROVEWHEELNAMES ? 1 : 0;
543+
}
544+
} else {
545+
// custom name
546+
nameCheck = settings::APPROVECUSTOMNAMES ? 1 : 0;
512547
}
513-
else if (!Database::isNameFree(AUTOU16TOU8(save->szFirstName), AUTOU16TOU8(save->szLastName))) {
514-
errorCode = 1;
548+
549+
if (errorCode == 0) {
550+
if (!CNLoginServer::isCharacterNameGood(firstName, lastName)) {
551+
errorCode = 4;
552+
}
553+
else if (!Database::isNameFree(firstName, lastName)) {
554+
errorCode = 1;
555+
}
515556
}
516557

517558
if (errorCode != 0) {
@@ -526,15 +567,12 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
526567
return;
527568
}
528569

529-
if (!Database::changeName(save, loginSessions[sock].userID))
570+
if (!Database::changeName(save->iPCUID, loginSessions[sock].userID, firstName.c_str(), lastName.c_str(), nameCheck))
530571
return invalidCharacter(sock);
531572

532-
Player plr;
533-
Database::getPlayer(&plr, (int)save->iPCUID);
534-
535573
// fire name check event if needed
536-
if (plr.PCStyle.iNameCheck != 1) {
537-
std::string namereq = std::to_string(save->iPCUID) + " " + AUTOU16TOU8(save->szFirstName) + " " + AUTOU16TOU8(save->szLastName);
574+
if (nameCheck != 1) {
575+
std::string namereq = std::to_string(save->iPCUID) + " " + firstName + " " + lastName;
538576
Monitor::namereqs.push_back(namereq);
539577
}
540578

@@ -550,8 +588,8 @@ void CNLoginServer::changeName(CNSocket* sock, CNPacketData* data) {
550588

551589
DEBUGLOG(
552590
std::cout << "Login Server: Name change request for character [" << save->iPCUID << "]" << std::endl;
553-
std::cout << "\tNew name: " << AUTOU16TOU8(save->szFirstName) << " " << AUTOU16TOU8(save->szLastName);
554-
if (plr.PCStyle.iNameCheck != 1) std::cout << " (pending approval)";
591+
std::cout << "\tNew name: " << firstName << " " << lastName;
592+
if (nameCheck != 1) std::cout << " (pending approval)";
555593
std::cout << std::endl;
556594
)
557595
}
@@ -645,6 +683,42 @@ bool CNLoginServer::isPasswordCorrect(std::string actualPassword, std::string tr
645683
return BCrypt::validatePassword(tryPassword, actualPassword);
646684
}
647685

686+
bool CNLoginServer::isNameWheelNameGood(int fnCode, int mnCode, int lnCode, std::string& firstName, std::string& lastName) {
687+
if (fnCode >= LoginServer::WheelFirstNames.size()
688+
|| mnCode >= LoginServer::WheelMiddleNames.size()
689+
|| lnCode >= LoginServer::WheelLastNames.size()) {
690+
std::cout << "[WARN] Login Server: Invalid name codes received: " << fnCode << " " << mnCode << " " << lnCode << std::endl;
691+
return false;
692+
}
693+
694+
// client sends 1 if not selected for these. they point to a single blank space. why.
695+
// just change them to 0, which points to an empty string; keeps the code much cleaner
696+
if (mnCode == 1) mnCode = 0;
697+
if (lnCode == 1) lnCode = 0;
698+
699+
std::string firstNameFromWheel = LoginServer::WheelFirstNames[fnCode];
700+
701+
std::string middleNamePart = LoginServer::WheelMiddleNames[mnCode];
702+
std::string lastNamePart = LoginServer::WheelLastNames[lnCode];
703+
if (mnCode != 0 && middleNamePart[middleNamePart.size() - 1] != ' ') {
704+
// If there's a middle name, we need to lowercase the last name
705+
std::transform(lastNamePart.begin(), lastNamePart.end(), lastNamePart.begin(), ::tolower);
706+
}
707+
std::string lastNameFromWheel = middleNamePart + lastNamePart;
708+
709+
if (firstNameFromWheel.empty() || lastNameFromWheel.empty()) {
710+
std::cout << "[WARN] Login Server: Invalid wheel name combo: " << fnCode << " " << mnCode << " " << lnCode << std::endl;
711+
return false;
712+
}
713+
714+
if (firstName != firstNameFromWheel || lastName != lastNameFromWheel) {
715+
std::cout << "[WARN] Login Server: Name wheel mismatch. Expected " << firstNameFromWheel << " " << lastNameFromWheel << ", got " << firstName << " " << lastName << std::endl;
716+
return false;
717+
}
718+
719+
return true;
720+
}
721+
648722
bool CNLoginServer::isCharacterNameGood(std::string Firstname, std::string Lastname) {
649723
//Allow alphanumeric and dot characters in names(disallows dot and space characters at the beginning of a name)
650724
std::regex firstnamecheck(R"(((?! )(?!\.)[a-zA-Z0-9]*\.{0,1}(?!\.+ +)[a-zA-Z0-9]* {0,1}(?! +))*$)");

src/servers/CNLoginServer.hpp

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77

88
#include <map>
99

10+
namespace LoginServer {
11+
extern std::vector<std::string> WheelFirstNames;
12+
extern std::vector<std::string> WheelMiddleNames;
13+
extern std::vector<std::string> WheelLastNames;
14+
}
15+
1016
struct CNLoginData {
1117
int userID;
1218
time_t lastHeartbeat;
@@ -51,6 +57,7 @@ class CNLoginServer : public CNServer {
5157
static bool isPasswordGood(std::string& password);
5258
static bool isPasswordCorrect(std::string actualPassword, std::string tryPassword);
5359
static bool isAccountInUse(int accountId);
60+
static bool isNameWheelNameGood(int fnCode, int mnCode, int lnCode, std::string& firstName, std::string& lastName);
5461
static bool isCharacterNameGood(std::string Firstname, std::string Lastname);
5562
static bool isAuthMethodAllowed(AuthMethod authMethod);
5663
static bool checkUsername(CNSocket* sock, std::string& username);

src/settings.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ bool settings::SANDBOX = true;
1212
std::string settings::SANDBOXEXTRAPATH = "";
1313

1414
int settings::LOGINPORT = 23000;
15-
bool settings::APPROVEALLNAMES = true;
15+
bool settings::APPROVEWHEELNAMES = true;
16+
bool settings::APPROVECUSTOMNAMES = true;
1617
bool settings::AUTOCREATEACCOUNTS = true;
1718
std::string settings::AUTHMETHODS = "password";
1819
int settings::DBSAVEINTERVAL = 240;
@@ -89,7 +90,8 @@ void settings::init() {
8990
SANDBOX = reader.GetBoolean("", "sandbox", SANDBOX);
9091
SANDBOXEXTRAPATH = reader.Get("", "sandboxextrapath", SANDBOXEXTRAPATH);
9192
LOGINPORT = reader.GetInteger("login", "port", LOGINPORT);
92-
APPROVEALLNAMES = reader.GetBoolean("login", "acceptallcustomnames", APPROVEALLNAMES);
93+
APPROVEWHEELNAMES = reader.GetBoolean("login", "acceptallwheelnames", APPROVEWHEELNAMES);
94+
APPROVECUSTOMNAMES = reader.GetBoolean("login", "acceptallcustomnames", APPROVECUSTOMNAMES);
9395
AUTOCREATEACCOUNTS = reader.GetBoolean("login", "autocreateaccounts", AUTOCREATEACCOUNTS);
9496
AUTHMETHODS = reader.Get("login", "authmethods", AUTHMETHODS);
9597
DBSAVEINTERVAL = reader.GetInteger("login", "dbsaveinterval", DBSAVEINTERVAL);

src/settings.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ namespace settings {
99
extern bool SANDBOX;
1010
extern std::string SANDBOXEXTRAPATH;
1111
extern int LOGINPORT;
12-
extern bool APPROVEALLNAMES;
12+
extern bool APPROVEWHEELNAMES;
13+
extern bool APPROVECUSTOMNAMES;
1314
extern bool AUTOCREATEACCOUNTS;
1415
extern std::string AUTHMETHODS;
1516
extern int DBSAVEINTERVAL;

0 commit comments

Comments
 (0)