#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Modules\\Logger.h" #include "Modules\\inicpp.hpp" #include "Modules\\dbActions.h" // #include "Modules/serverFrontEndBridge.h" //#include "Modules/datapackProcess.h" #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "sqlite3.lib") using inicpp::ini; using inicpp::IniManager; using std::string; using std::to_string; using std::thread; using std::vector; using std::mt19937; using std::random_device; using std::uniform_int_distribution; using std::filesystem::create_hard_link; using std::filesystem::read_symlink; using std::stoi; int isDebug = 1; string db_path = ".\\database.db"; sqlite3* db; int tokenLength = 30; // Length of user token struct structUserInfo { int ID; string name[65525]; string passwd[65525]; int port_IDMap[65525]; int ID_PortMap[65525]; string ID_TokenMap[65525]; } userInfo; struct structIpAddr { const char* server_cstr = "127.0.0.1"; struct in_addr server; vector server_vec; int listenPort = 23000; } ipAddr; struct publicSock { SOCKET client; SOCKET service; SOCKET sysAdmin; } globalSock; int serverInit_Sockets(); // Initialize socket void userExit(int userID, int clientPort); // Process user exit event string getTextMiddle(string source, string left, string right); // Get a text from a text bool tcpSend(SOCKET sock, string str); bool verifyTimestamp(const string& timestampStr); string getRandomStr(const int len); int serverInit_SQLite_Open(); int serverInit_Sockets(); // UTF-8 encoding helper functions bool isValidUtf8(const char* str, size_t length); string ensureUtf8(const string& str); int main() { postLog("[System] Starting Teacher Salary Management Server...", 1); if (serverInit_SQLite_Open() != 0) { postLog("[System] Failed to initialize SQLite database.", 4); return 1; } serverInit_Sockets(); closesocket(globalSock.client); WSACleanup(); } // Check if the given string is valid UTF-8 bool isValidUtf8(const char* str, size_t length) { size_t i = 0; while (i < length) { unsigned char c = static_cast(str[i]); if (c <= 0x7F) { i++; } else if (c >= 0xC2 && c <= 0xDF) { if (i + 1 >= length || (static_cast(str[i + 1]) & 0xC0) != 0x80) return false; i += 2; } else if (c == 0xE0) { if (i + 2 >= length || (static_cast(str[i + 1]) & 0xC0) != 0x80 || static_cast(str[i + 1]) < 0xA0 || (static_cast(str[i + 2]) & 0xC0) != 0x80) return false; i += 3; } else if (c >= 0xE1 && c <= 0xEC) { if (i + 2 >= length || (static_cast(str[i + 1]) & 0xC0) != 0x80 || (static_cast(str[i + 2]) & 0xC0) != 0x80) return false; i += 3; } else if (c == 0xED) { if (i + 2 >= length || (static_cast(str[i + 1]) & 0xC0) != 0x80 || static_cast(str[i + 1]) > 0x9F || (static_cast(str[i + 2]) & 0xC0) != 0x80) return false; i += 3; } else if (c >= 0xEE && c <= 0xEF) { if (i + 2 >= length || (static_cast(str[i + 1]) & 0xC0) != 0x80 || (static_cast(str[i + 2]) & 0xC0) != 0x80) return false; i += 3; } else if (c == 0xF0) { if (i + 3 >= length || (static_cast(str[i + 1]) & 0xC0) != 0x80 || static_cast(str[i + 1]) < 0x90 || (static_cast(str[i + 2]) & 0xC0) != 0x80 || (static_cast(str[i + 3]) & 0xC0) != 0x80) return false; i += 4; } else if (c >= 0xF1 && c <= 0xF3) { if (i + 3 >= length || (static_cast(str[i + 1]) & 0xC0) != 0x80 || (static_cast(str[i + 2]) & 0xC0) != 0x80 || (static_cast(str[i + 3]) & 0xC0) != 0x80) return false; i += 4; } else if (c == 0xF4) { if (i + 3 >= length || (static_cast(str[i + 1]) & 0xC0) != 0x80 || static_cast(str[i + 1]) > 0x8F || (static_cast(str[i + 2]) & 0xC0) != 0x80 || (static_cast(str[i + 3]) & 0xC0) != 0x80) return false; i += 4; } else { return false; } } return true; } // Ensure string is valid UTF-8, replace invalid sequences if needed string ensureUtf8(const string& str) { if (str.empty()) return str; // Check if the string is already valid UTF-8 if (isValidUtf8(str.c_str(), str.length())) { return str; } // If invalid UTF-8, try to convert from common encodings // This is a simple fallback for invalid UTF-8 string result; for (size_t i = 0; i < str.length(); i++) { unsigned char c = static_cast(str[i]); if (c <= 0x7F) { result += str[i]; } else { // Replace non-ASCII characters with '?' for simplicity // In a production system, you'd want proper encoding detection and conversion result += '?'; } } postLog("[Encoding] Invalid UTF-8 detected, replaced non-ASCII characters", 2); return result; } void handle_client(SOCKET sock, string clientIP, int clientPort) { constexpr int MAX_USERS = 65525; auto inRange = [&](int idx) { return idx >= 0 && idx < MAX_USERS; }; char buffer[1024]; int bytesReceived; postLog("[Socket] New connection from " + clientIP + ":" + to_string(clientPort), 0); bool keepRunning = true; while (keepRunning) { bytesReceived = recv(sock, buffer, sizeof(buffer), 0); if (bytesReceived > 0) { // Normal data received string received(buffer, bytesReceived); if (received.empty()) { postLog("[Socket] Received empty payload, closing socket.", 3); break; } // Ensure received data is valid UTF-8 string processedReceived = ensureUtf8(received); if (processedReceived != received) { postLog("[Socket] Received data contains invalid UTF-8, processed", 2); } size_t leftControlSign = processedReceived.find("["); size_t rightControlSign = processedReceived.find("]"); if (leftControlSign != string::npos && rightControlSign != string::npos && rightControlSign > leftControlSign) { string cmdType = processedReceived.substr(leftControlSign + 1, rightControlSign - leftControlSign - 1); postLog("[Socket] Command received: " + cmdType, 0); size_t argStart = processedReceived.find_first_not_of(" ", rightControlSign + 1); string argument = (argStart == string::npos) ? string() : processedReceived.substr(argStart); postLog("[Socket] Argument received: " + argument, 0); // debug — keep connection open if (cmdType == "debug") { continue; } // new user registration — keep connection open on errors, close after successful register if (cmdType == "newUserRegist") { string newUserName = getTextMiddle(argument, "", ""); string newUserPasswd = getTextMiddle(argument, "", ""); string newUsertimeStamp = getTextMiddle(argument, "", ""); int newUserID = -1; if (newUserName.empty() || newUserPasswd.empty() || newUsertimeStamp.empty()) { tcpSend(sock, "[Regist_Rejected] Invalid registration information."); postLog("[User.Auth] Received invalid regist request.", 2); continue; } if (!verifyTimestamp(newUsertimeStamp)) { tcpSend(sock, "[Regist_Rejected] Invalid timestamp."); postLog("[User.Auth] Received invalid timestamp: " + newUsertimeStamp, 2); continue; } if (newUserName.length() > 20 || newUserPasswd.length() > 20) { tcpSend(sock, "[Regist_Rejected] Username or password too long."); postLog("[User.Auth] Received too long username or password: " + newUserName + " / " + newUserPasswd, 2); continue; } for (int i = 1; i < MAX_USERS; i++) { if (db_getItem(db, "users", "userID", i) != "") { continue; } else { newUserID = i; break; } } if (newUserID == -1) { tcpSend(sock, "[Regist_Rejected] User limit reached."); postLog("[User.Auth] No available userID found, user limit reached.", 2); continue; } if (db_getItem(db, "users", "userName", newUserID) != "") { tcpSend(sock, "[Regist_Rejected] Username already existed."); postLog("[User.Auth] Username already existed.", 2); continue; } postLog("[Socket] Receive legal regist request - ID: " + to_string(newUserID) + ", Name: " + newUserName + ", userPasswd: " + newUserPasswd, 1); //db_addItem(db, "users", newUserID, "userName", newUserName); //db_addItem(db, "users", newUserID, "Passwd", newUserPasswd); tcpSend(sock, "[Regist_Accepted] User registered successfully."); postLog("[Socket] New user registed: " + newUserName, 1); // close connection after successful registration break; } /* User Login Args: - userName - userPasswd - timeStamp */ if (cmdType == "newUserEntry") { int newUserID; string newUserName = getTextMiddle(argument, "", ""); string newUserPasswd = getTextMiddle(argument, "", ""); string newUsertimeStamp = getTextMiddle(argument, "", ""); if (newUserName == "" || newUserPasswd == "" || newUsertimeStamp == "") { tcpSend(sock, "[Login_Rejected] Invalid registration information."); postLog("[Socket] Received invalid regist request.", 2); continue; } if (!verifyTimestamp(newUsertimeStamp)) { tcpSend(sock, "[Login_Rejected] Invalid timestamp."); postLog("[Socket] Received invalid timestamp: " + newUsertimeStamp, 2); continue; } if (newUserName.length() > 20 || newUserPasswd.length() > 20) { tcpSend(sock, "[Login_Rejected] Username or password too long."); postLog("[Socket] Received too long username or password: " + newUserName + " / " + newUserPasswd, 2); continue; } newUserID = stoi(db_findIdBy(db, "users", "userName", newUserName)); if (newUserID != 0) { if (!(newUserName == db_getItem(db, "users", "userName", newUserID) && newUserPasswd == db_getItem(db, "users", "Passwd", newUserID))) { tcpSend(sock, "[Login_Rejected] Invalid username or password."); postLog("[Socket] Invalid username or password for user: " + newUserName, 2); continue; } } else { tcpSend(sock, "[Login_Rejected] User does not exist."); postLog("[Socket] User does not exist: " + newUserName, 2); continue; } postLog("[Socket] Receive loggin request - ID: " + to_string(newUserID) + ", Name: " + newUserName + ", userPasswd: " + newUserPasswd, 1); // New user logged in successfully — keep connection open postLog("[Socket] New user entry: " + newUserName, 1); postLog("[Socket] userInfo.ID_PortMap[" + to_string(clientPort) + "] = " + to_string(userInfo.ID_PortMap[clientPort]), 0); if (userInfo.ID_PortMap[newUserID] == 0) { // Record user info to specific port userInfo.name[newUserID] = newUserName; userInfo.passwd[newUserID] = newUserPasswd; userInfo.port_IDMap[clientPort] = newUserID; userInfo.ID_PortMap[newUserID] = clientPort; userInfo.ID_TokenMap[newUserID] = getRandomStr(tokenLength); // Generate token tcpSend(sock, "[Login_Accepted] " + userInfo.name[newUserID] + " " + to_string(newUserID) + " " + userInfo.ID_TokenMap[newUserID] + ""); postLog("[Socket] User [" + to_string(newUserID) + "]" + newUserName + ":" + to_string(clientPort) + " logged in successfully.", 1); // keep the connection active to continue monitoring disconnects continue; } else { postLog("[Socket] User [" + to_string(newUserID) + "]" + newUserName + ":" + to_string(clientPort) + " already existed: [" + to_string(newUserID) + "]" + userInfo.name[newUserID] + ":" + to_string(userInfo.ID_PortMap[newUserID]), 2); tcpSend(sock, "[Login_Rejected] User already logged in."); continue; } } if (cmdType == "userExit") { // User logout int uid = userInfo.port_IDMap[clientPort]; if (getTextMiddle(argument, "", "") != userInfo.ID_TokenMap[uid]) { // Token verification failed tcpSend(sock, "[Exit_Rejected] Invalid token."); postLog("[Socket] User [" + to_string(uid) + "]" + userInfo.name[uid] + " exit rejected due to invalid token.", 2); continue; } if (getTextMiddle(argument, "", "") != to_string(uid)) { // UserID verification failed tcpSend(sock, "[Exit_Rejected] Invalid userID."); postLog("[Socket] User [" + to_string(uid) + "]" + userInfo.name[uid] + " exit rejected due to invalid userID.", 2); continue; } postLog("[Socket] User [" + userInfo.name[uid] + "] Exited normally.", 1); userExit(userInfo.port_IDMap[clientPort], clientPort); tcpSend(sock, "[Exit_Accepted] User exited successfully."); // close connection after normal exit break; } if (cmdType == "userGet") { int getUserID = stoi(getTextMiddle(argument, "", "")); // Get request parameters string getToken = getTextMiddle(argument, "", ""); string getTimeStamp = getTextMiddle(argument, "", ""); string getTable = getTextMiddle(argument, "", "
"); string getItemID = getTextMiddle(argument, "", ""); string getValueType = getTextMiddle(argument, "", ""); if (getUserID == 0 || getToken == "" || getTimeStamp == "" || getTable == "" || getItemID == "" || getValueType == "") { tcpSend(sock, "[Get_Rejected] Invalid get information."); postLog("[Socket] Received invalid get request.", 2); continue; } if (!verifyTimestamp(getTimeStamp)) { tcpSend(sock, "[Get_Rejected] Invalid timestamp."); postLog("[Socket] Received invalid timestamp: " + getTimeStamp, 2); continue; } postLog("[Socket] Receive get request - userID: " + to_string(getUserID) + ", table: " + getTable + ", itemID: " + getItemID + ", valueType: " + getValueType, 1); if (getToken != userInfo.ID_TokenMap[getUserID]) { tcpSend(sock, "[Get_Rejected] Invalid token."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to invalid token.", 2); continue; } if ((char)getItemID.c_str() <= '9' && (char)getItemID.c_str() >= '0') { getItemID = db_findIdBy(db, getTable, "teacherName", getItemID); if (getItemID == "0") { tcpSend(sock, "[Get_Rejected] ItemID does not exist."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2); continue; } } if (db_getItem(db, getTable, "teacherName", stoi(getItemID)) == "err: no specific id") { tcpSend(sock, "[Get_Rejected] ItemID does not exist."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2); continue; } if (getTable == "salaryForm") { if (getValueType == "all") { if (db_getItem(db, "salaryForm", "teacherName", stoi(getItemID)) != "") { string basicSalary = db_getItem(db, "salaryForm", "basicSalary", stoi(getItemID)); string allowances = db_getItem(db, "salaryForm", "allowances", stoi(getItemID)); string livingAllowances = db_getItem(db, "salaryForm", "livingAllowances", stoi(getItemID)); string phoneCost = db_getItem(db, "salaryForm", "phoneCost", stoi(getItemID)); string waterPowerCost = db_getItem(db, "salaryForm", "waterPowerCost", stoi(getItemID)); string houseRent = db_getItem(db, "salaryForm", "houseRent", stoi(getItemID)); string incomeTax = db_getItem(db, "salaryForm", "incomeTax", stoi(getItemID)); string cleaningCost = db_getItem(db, "salaryForm", "cleaningCost", stoi(getItemID)); string housingFund = db_getItem(db, "salaryForm", "housingFund", stoi(getItemID)); tcpSend(sock, "[Get_Accepted] " + getItemID + " " + basicSalary + " " + allowances + " " + livingAllowances + " " + phoneCost + " " + waterPowerCost + " " + houseRent + " " + incomeTax + " " + cleaningCost + " " + housingFund + ""); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get all values successfully.", 1); } else { tcpSend(sock, "[Get_Rejected] ItemID does not exist."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2); } } else { if (db_getItem(db, "salaryForm", getValueType, stoi(getItemID)) != "") { string getValue = db_getItem(db, "salaryForm", getValueType, stoi(getItemID)); tcpSend(sock, "[Get_Accepted] " + getValue + ""); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get value successfully: " + getValue, 1); } else { tcpSend(sock, "[Get_Rejected] ItemID does not exist."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2); } } } if (getTable == "teacherInfo") { if (getValueType == "all") { if (db_getItem(db, "teacherInfo", "teacherName", stoi(getItemID)) != "") { string teacherName = db_getItem(db, "teacherInfo", "teacherName", stoi(getItemID)); string gender = db_getItem(db, "teacherInfo", "gender", stoi(getItemID)); string companyName = db_getItem(db, "teacherInfo", "companyName", stoi(getItemID)); string address = db_getItem(db, "teacherInfo", "address", stoi(getItemID)); string tel = db_getItem(db, "teacherInfo", "tel", stoi(getItemID)); tcpSend(sock, "[Get_Accepted] " + getItemID + " " + teacherName + " " + gender + " " + companyName + "
" + address + "
" + tel + ""); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get all values successfully.", 1); } else { tcpSend(sock, "[Get_Rejected] ItemID does not exist."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2); } } else { if (db_getItem(db, "teacherInfo", getValueType, stoi(getItemID)) != "") { string getValue = db_getItem(db, "teacherInfo", getValueType, stoi(getItemID)); tcpSend(sock, "[Get_Accepted] " + getValue + ""); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get value successfully: " + getValue, 1); } else { tcpSend(sock, "[Get_Rejected] ItemID does not exist."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to non-existent itemID: " + getItemID, 2); } } } continue; } if (cmdType == "userSet") { int getUserID = stoi(getTextMiddle(argument, "", "")); string getToken = getTextMiddle(argument, "", " "); string getTimeStamp = getTextMiddle(argument, "", ""); string getTable = getTextMiddle(argument, "", "
"); string getItemID = getTextMiddle(argument, "", ""); string getValueType = getTextMiddle(argument, "", ""); string getValue = getTextMiddle(argument, "", ""); if (getUserID == 0 || getToken == "" || getTimeStamp == "" || getTable == "" || getItemID == "" || getValueType == "" || getValue == "") { tcpSend(sock, "[Set_Rejected] Invalid set information."); postLog("[Socket] Received invalid set request.", 2); continue; } if (!verifyTimestamp(getTimeStamp)) { tcpSend(sock, "[Set_Rejected] Invalid timestamp."); postLog("[Socket] Received invalid timestamp: " + getTimeStamp, 2); continue; } postLog("[Socket] Receive set request - userID: " + to_string(getUserID) + ", table: " + getTable + ", itemID: " + getItemID + ", valueType: " + getValueType, 1); if (getToken != userInfo.ID_TokenMap[getUserID]) { tcpSend(sock, "[Set_Rejected] Invalid token."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " get rejected due to invalid token.", 2); continue; } if (db_getItem(db, getTable, "teacherName", stoi(getItemID)) == "err: no specific id") { postLog("[Database] No such itemID[" + getItemID + "] in " + getTable + " table: " + getTable + ", create a new one.", 2); db_addItem(db, getTable, stoi(getItemID), "teacherID", getItemID); // for (int i = 0; i < MAX_USERS; i++) { // if (db_getItem(db, "salaryForm", "teacherName", i) == "err: no specific id") { // getItemID = i; // // break; // } // else { // tcpSend(sock, "[Set_Rejected] Database is full."); // postLog("[Database] salaryForm table is full, cannot add new item.", 2); // return; // } // } } if (db_updateItem(db, getTable, stoi(getItemID), getValueType, getValue)) { tcpSend(sock, "[Set_Accepted] Value set successfully."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " set value successfully: " + getValueType + " = " + getValue, 1); continue; } else { tcpSend(sock, "[Set_Rejected] Failed to set value."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " set value failed: " + getValueType + " = " + getValue, 2); continue; } } if (cmdType == "userDel") { int getUserID = stoi(getTextMiddle(argument, "", "")); string getToken = getTextMiddle(argument, "", " "); string getTimeStamp = getTextMiddle(argument, "", ""); string getTable = getTextMiddle(argument, "", "
"); string getItemID = getTextMiddle(argument, "", ""); if (getUserID == 0 || getToken == "" || getTimeStamp == "" || getTable == "" || getItemID == "") { tcpSend(sock, "[Del_Rejected] Invalid delete information."); postLog("[Socket] Received invalid delete request.", 2); continue; } if (!verifyTimestamp(getTimeStamp)) { tcpSend(sock, "[Del_Rejected] Invalid timestamp."); postLog("[Socket] Received invalid timestamp: " + getTimeStamp, 2); continue; } postLog("[Socket] Receive delete request - userID: " + to_string(getUserID) + ", table: " + getTable + ", itemID: " + getItemID, 1); if (getToken != userInfo.ID_TokenMap[getUserID]) { tcpSend(sock, "[Del_Rejected] Invalid token."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " delete rejected due to invalid token.", 2); continue; } if (db_rmItem(db, getTable, stoi(getItemID))) { tcpSend(sock, "[Del_Accepted] Item deleted successfully."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " deleted itemID " + getItemID + " successfully.", 1); continue; } else { tcpSend(sock, "[Del_Rejected] Failed to delete item."); postLog("[Socket] User [" + to_string(getUserID) + "]" + userInfo.name[getUserID] + " failed to delete itemID " + getItemID + ".", 2); continue; } } // Add cmdType handlers above this line tcpSend(sock, "[Error] Unknown command: " + cmdType); postLog("[Socket] Unknown command received: " + cmdType, 2); continue; } else { // Non-command payload; ignore but do not treat as disconnect. postLog("[Socket] Non-command data received from " + clientIP + ":" + to_string(clientPort) + " (" + to_string(bytesReceived) + " bytes).", 1); continue; } } else if (bytesReceived == 0) { // orderly shutdown by peer postLog("[Socket] Client closed connection (graceful): " + clientIP + ":" + to_string(clientPort), 0); break; } else { // bytesReceived == SOCKET_ERROR int err = WSAGetLastError(); postLog("[Socket] recv() returned SOCKET_ERROR: " + to_string(err), 2); if (err == WSAEWOULDBLOCK) { // If using non-blocking sockets, wait with select() to avoid busy-looping. fd_set readfds; FD_ZERO(&readfds); FD_SET(sock, &readfds); timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; int sel = select(0, &readfds, NULL, NULL, &tv); if (sel == 0) { continue; } if (sel == SOCKET_ERROR) { postLog("[Socket] select() failed: " + to_string(WSAGetLastError()), 2); break; } continue; } else if (err == WSAECONNRESET) { postLog("[Socket] Connection reset by peer: " + clientIP + ":" + to_string(clientPort), 2); break; } else { postLog("[Socket] recv() fatal error, closing connection: " + to_string(err), 2); break; } } } // Common cleanup path postLog("[Socket] Connection cleanup for " + clientIP + ":" + to_string(clientPort), 0); postLog("[Socket] userPortMap[" + to_string(clientPort) + "] = " + to_string(userInfo.port_IDMap[clientPort]), 0); if (userInfo.port_IDMap[clientPort] != 0) { postLog("[Socket] User [" + userInfo.name[userInfo.port_IDMap[clientPort]] + "] Exited without notification!", 2); userExit(userInfo.port_IDMap[clientPort], clientPort); } shutdown(sock, SD_BOTH); closesocket(sock); } int serverInit_Sockets() { if (inet_pton(AF_INET, ipAddr.server_cstr, &ipAddr.server) != 1) { postLog("[Socket] Failed to convert IP Address.", 4); return 1; } WSADATA wsaData; if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { postLog("[Socket] WSAStartup failed.", 4); return 1; } SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, 0); // Create main server socket if (listenSocket == INVALID_SOCKET) { postLog("[Socket] Failed to create socket.", 4); WSACleanup(); return 1; } sockaddr_in serverAddr = {}; serverAddr.sin_family = AF_INET; serverAddr.sin_addr = ipAddr.server; serverAddr.sin_port = htons(ipAddr.listenPort); if (bind(listenSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR) { postLog("[Socket.service] Bind failed.", 4); closesocket(listenSocket); WSACleanup(); return 1; } if (listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) { postLog("[Socket] Listen failed.", 4); closesocket(listenSocket); WSACleanup(); return 1; } // log the configured server IP string instead of unused server_vec postLog("[Socket] Server is listening on " + string(ipAddr.server_cstr) + ":" + to_string(ipAddr.listenPort) + ".", 1); postLog("[System] Initialization complete. Server is now running.", 1); // Accept loop: create and detach threads immediately while (true) { sockaddr_in clientAddr; int clientAddrSize = sizeof(clientAddr); SOCKET clientSocket_serviceSock = accept(listenSocket, (sockaddr*)&clientAddr, &clientAddrSize); if (clientSocket_serviceSock != INVALID_SOCKET) { globalSock.service = clientSocket_serviceSock; char ipStr[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &(clientAddr.sin_addr), ipStr, INET_ADDRSTRLEN); int clientPort = ntohs(clientAddr.sin_port); std::thread t(handle_client, clientSocket_serviceSock, string(ipStr), clientPort); t.detach(); // let thread run independently } } } void userExit(int userID, int clientPort) { // User logout processing function userInfo.name[userID] = ""; userInfo.ID_PortMap[userID] = 0; userInfo.ID_TokenMap[userID] = ""; userInfo.port_IDMap[clientPort] = 0; } string getTextMiddle(string source, string left, string right) { // Get content between left and right strings size_t leftPos = source.find(left); if (leftPos == string::npos) return ""; leftPos += left.length(); size_t rightPos = source.find(right, leftPos); if (rightPos == string::npos) return ""; return source.substr(leftPos, rightPos - leftPos); } bool tcpSend(SOCKET sock, string str) { // Ensure the string is valid UTF-8 before sending string strToSend = ensureUtf8(str); if (strToSend != str) { postLog("[Encoding] Fixed UTF-8 encoding before sending", 1); } int r = send(sock, strToSend.c_str(), (int)strToSend.size(), 0); if (r == SOCKET_ERROR) { postLog("[method.tcpSend] send() failed: " + to_string(WSAGetLastError()), 4); return false; } return true; } bool verifyTimestamp(const string& timestampStr) { // Verify timestamp validity try { int timestamp = stoi(timestampStr); int currentTimestamp = static_cast(time(nullptr)); if (timestamp <= currentTimestamp + 300 && timestamp >= currentTimestamp - 300) { return true; // Timestamp within allowed range } else { return false; // Invalid timestamp } } catch (...) { return false; // Conversion failed, invalid timestamp } } int serverInit_SQLite_Open() { char* errMsg = nullptr; int if_db_opened = sqlite3_open(db_path.c_str(), &db); if (if_db_opened) { postLog("[SQLite] Failed to open database", 4); return if_db_opened; } postLog("[SQLite] Creating databases...", 1); const char* sql_create_users = "CREATE TABLE IF NOT EXISTS users (userId INT, userName TEXT, Passwd TEXT, PRIMARY KEY(userID, userName));"; const char* sql_create_salaryForm = "CREATE TABLE IF NOT EXISTS salaryForm (teacherID INT , teacherName TEXT, basicSalary INT, allowances INT, livingAllowances INT, phoneCost INT, waterPowerCost INT, houseRent INT, incomeTax INT, cleaningCost INT, housingFund INT, PRIMARY KEY(teacherID, teacherName));"; const char* sql_create_teacherInfo = "CREATE TABLE IF NOT EXISTS teacherInfo (teacherID INT , teacherName TEXT, gender TXT, companyName TXT, address TXT, tel TXT, PRIMARY KEY(teacherID, teacherName));"; if (sqlite3_exec(db, sql_create_users, 0, 0, &errMsg) != SQLITE_OK) { postLog("[SQLite] SQL error (users): " + string(errMsg), 4); sqlite3_free(errMsg); } if (sqlite3_exec(db, sql_create_salaryForm, 0, 0, &errMsg) != SQLITE_OK) { postLog("[SQLite] SQL error (salaryForm): " + string(errMsg), 4); sqlite3_free(errMsg); } if (sqlite3_exec(db, sql_create_teacherInfo, 0, 0, &errMsg) != SQLITE_OK) { postLog("[SQLite] SQL error (teacherInfo): " + string(errMsg), 4); sqlite3_free(errMsg); } } string getRandomStr(const int len) { // Generate random string static const char charset[] = "0123456789" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"; static thread_local mt19937 rg{ random_device{}() }; static thread_local uniform_int_distribution<> pick(0, sizeof(charset) - 2); string s; s.reserve(len); for (int i = 0; i < len; ++i) s += charset[pick(rg)]; return s; }