upload all files

This commit is contained in:
2026-01-07 21:21:28 +08:00
parent 09d49d22c9
commit 8f53d867a2
220 changed files with 419218 additions and 0 deletions

742
TeacherSalaryMgmt.cpp Normal file
View File

@@ -0,0 +1,742 @@
#include <string>
#include <WS2tcpip.h>
#include <WinSock2.h>
#include <windows.h>
#include <iostream>
#include <thread>
#include <vector>
#include <random>
#include <sstream>
#include <fstream>
#include <openssl\\rand.h>
#include <sqlite3.h>
#include <filesystem>
#include <mutex>
#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<char> 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<unsigned char>(str[i]);
if (c <= 0x7F) {
i++;
}
else if (c >= 0xC2 && c <= 0xDF) {
if (i + 1 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80) return false;
i += 2;
}
else if (c == 0xE0) {
if (i + 2 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
static_cast<unsigned char>(str[i + 1]) < 0xA0 ||
(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80) return false;
i += 3;
}
else if (c >= 0xE1 && c <= 0xEC) {
if (i + 2 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80) return false;
i += 3;
}
else if (c == 0xED) {
if (i + 2 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
static_cast<unsigned char>(str[i + 1]) > 0x9F ||
(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80) return false;
i += 3;
}
else if (c >= 0xEE && c <= 0xEF) {
if (i + 2 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80) return false;
i += 3;
}
else if (c == 0xF0) {
if (i + 3 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
static_cast<unsigned char>(str[i + 1]) < 0x90 ||
(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80 ||
(static_cast<unsigned char>(str[i + 3]) & 0xC0) != 0x80) return false;
i += 4;
}
else if (c >= 0xF1 && c <= 0xF3) {
if (i + 3 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80 ||
(static_cast<unsigned char>(str[i + 3]) & 0xC0) != 0x80) return false;
i += 4;
}
else if (c == 0xF4) {
if (i + 3 >= length || (static_cast<unsigned char>(str[i + 1]) & 0xC0) != 0x80 ||
static_cast<unsigned char>(str[i + 1]) > 0x8F ||
(static_cast<unsigned char>(str[i + 2]) & 0xC0) != 0x80 ||
(static_cast<unsigned char>(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<unsigned char>(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, "<userName>", "</userName>");
string newUserPasswd = getTextMiddle(argument, "<userPasswd>", "</userPasswd>");
string newUsertimeStamp = getTextMiddle(argument, "<timeStamp>", "</timeStamp>");
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, "<userName>", "</userName>");
string newUserPasswd = getTextMiddle(argument, "<userPasswd>", "</userPasswd>");
string newUsertimeStamp = getTextMiddle(argument, "<timeStamp>", "</timeStamp>");
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] <userName>" + userInfo.name[newUserID] + "</userName> <userID>" + to_string(newUserID) + "</userID> <token>" + userInfo.ID_TokenMap[newUserID] + "</token>");
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, "<token>", "</token>") != 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, "<userID>", "</userID>") != 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, "<userID>", "</userID>")); // Get request parameters
string getToken = getTextMiddle(argument, "<token>", "</token>");
string getTimeStamp = getTextMiddle(argument, "<timeStamp>", "</timeStamp>");
string getTable = getTextMiddle(argument, "<table>", "</table>");
string getItemID = getTextMiddle(argument, "<itemID>", "</itemID>");
string getValueType = getTextMiddle(argument, "<valueType>", "</valueType>");
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] <itemID>" + getItemID + "</itemID> <basicSalary>" + basicSalary + "</basicSalary> <allowances>" + allowances + "</allowances> <livingAllowances>" + livingAllowances + "</livingAllowances> <phoneCost>" + phoneCost + "</phoneCost> <waterPowerCost>" + waterPowerCost + "</waterPowerCost> <houseRent>" + houseRent + "</houseRent> <incomeTax>" + incomeTax + "</incomeTax> <cleaningCost>" + cleaningCost + "</cleaningCost> <housingFund>" + housingFund + "</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] <value>" + getValue + "</value>");
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] <itemID>" + getItemID + "</itemID> <teacherName>" + teacherName + "</teacherName> <gender>" + gender + "</gender> <companyName>" + companyName + "</companyName> <address>" + address + "</address> <tel>" + tel + "</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] <value>" + getValue + "</value>");
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, "<userID>", "</userID>"));
string getToken = getTextMiddle(argument, "<token>", "</token> ");
string getTimeStamp = getTextMiddle(argument, "<timeStamp>", "</timeStamp>");
string getTable = getTextMiddle(argument, "<table>", "</table>");
string getItemID = getTextMiddle(argument, "<itemID>", "</itemID>");
string getValueType = getTextMiddle(argument, "<valueType>", "</valueType>");
string getValue = getTextMiddle(argument, "<value>", "</value>");
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, "<userID>", "</userID>"));
string getToken = getTextMiddle(argument, "<token>", "</token> ");
string getTimeStamp = getTextMiddle(argument, "<timeStamp>", "</timeStamp>");
string getTable = getTextMiddle(argument, "<table>", "</table>");
string getItemID = getTextMiddle(argument, "<itemID>", "</itemID>");
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<int>(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;
}