#include "database.h" #include #include namespace scar { Database::Database(const std::string& db_path) : db_(nullptr), db_path_(db_path) {} Database::~Database() { if (db_) { sqlite3_close(db_); } } bool Database::initialize() { int rc = sqlite3_open(db_path_.c_str(), &db_); if (rc != SQLITE_OK) { std::cerr << "Cannot open database: " << sqlite3_errmsg(db_) << std::endl; return false; } const char* create_table_sql = R"( CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password TEXT NOT NULL, salt TEXT NOT NULL, token TEXT, status TEXT DEFAULT 'offline', role TEXT, email TEXT, last_login INTEGER, avatar_pic BLOB ); CREATE INDEX IF NOT EXISTS idx_username ON users(username); CREATE INDEX IF NOT EXISTS idx_token ON users(token); )"; return execute(create_table_sql); } bool Database::execute(const std::string& sql) { char* err_msg = nullptr; int rc = sqlite3_exec(db_, sql.c_str(), nullptr, nullptr, &err_msg); if (rc != SQLITE_OK) { std::cerr << "SQL error: " << err_msg << std::endl; sqlite3_free(err_msg); return false; } return true; } bool Database::createUser(const std::string& username, const std::string& password_hash, const std::string& salt) { const char* sql = "INSERT INTO users (username, password, salt) VALUES (?, ?, ?)"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return false; } sqlite3_bind_text(stmt, 1, username.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, password_hash.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 3, salt.c_str(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } std::unique_ptr Database::getUserByUsername(const std::string& username) { const char* sql = "SELECT id, username, password, salt, token, status, role, email, last_login FROM users WHERE username = ?"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return nullptr; } sqlite3_bind_text(stmt, 1, username.c_str(), -1, SQLITE_TRANSIENT); std::unique_ptr user; if (sqlite3_step(stmt) == SQLITE_ROW) { user = std::make_unique(); user->id = sqlite3_column_int(stmt, 0); user->username = reinterpret_cast(sqlite3_column_text(stmt, 1)); user->password_hash = reinterpret_cast(sqlite3_column_text(stmt, 2)); user->salt = reinterpret_cast(sqlite3_column_text(stmt, 3)); const char* token = reinterpret_cast(sqlite3_column_text(stmt, 4)); user->token = token ? token : ""; const char* status_str = reinterpret_cast(sqlite3_column_text(stmt, 5)); user->status = (status_str && std::string(status_str) == "online") ? UserStatus::ONLINE : UserStatus::OFFLINE; const char* role = reinterpret_cast(sqlite3_column_text(stmt, 6)); user->role = role ? role : ""; const char* email = reinterpret_cast(sqlite3_column_text(stmt, 7)); user->email = email ? email : ""; user->last_login = sqlite3_column_int64(stmt, 8); } sqlite3_finalize(stmt); return user; } bool Database::updateUserToken(const std::string& username, const std::string& token) { const char* sql = "UPDATE users SET token = ? WHERE username = ?"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return false; } sqlite3_bind_text(stmt, 1, token.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } bool Database::updateUserStatus(const std::string& username, UserStatus status) { const char* status_str = (status == UserStatus::ONLINE) ? "online" : "offline"; const char* sql = "UPDATE users SET status = ? WHERE username = ?"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return false; } sqlite3_bind_text(stmt, 1, status_str, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } bool Database::updateLastLogin(const std::string& username) { int64_t now = std::time(nullptr); const char* sql = "UPDATE users SET last_login = ? WHERE username = ?"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return false; } sqlite3_bind_int64(stmt, 1, now); sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } bool Database::verifyCredentials(const std::string& username, const std::string& password_hash) { auto user = getUserByUsername(username); return user && user->password_hash == password_hash; } std::string Database::getUsernameByToken(const std::string& token) { const char* sql = "SELECT username FROM users WHERE token = ?"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return ""; } sqlite3_bind_text(stmt, 1, token.c_str(), -1, SQLITE_TRANSIENT); std::string username; if (sqlite3_step(stmt) == SQLITE_ROW) { username = reinterpret_cast(sqlite3_column_text(stmt, 0)); } sqlite3_finalize(stmt); return username; } bool Database::deleteUser(const std::string& username) { const char* sql = "DELETE FROM users WHERE username = ?"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return false; } sqlite3_bind_text(stmt, 1, username.c_str(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } bool Database::updateUserPassword(const std::string& username, const std::string& password_hash, const std::string& salt) { const char* sql = "UPDATE users SET password = ?, salt = ? WHERE username = ?"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return false; } sqlite3_bind_text(stmt, 1, password_hash.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, salt.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 3, username.c_str(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } bool Database::updateUserAvatar(const std::string& username, const std::vector& avatar_data) { const char* sql = "UPDATE users SET avatar_pic = ? WHERE username = ?"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return false; } sqlite3_bind_blob(stmt, 1, avatar_data.data(), avatar_data.size(), SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } bool Database::updateUserEmail(const std::string& username, const std::string& email) { const char* sql = "UPDATE users SET email = ? WHERE username = ?"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return false; } sqlite3_bind_text(stmt, 1, email.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } bool Database::updateUserRole(const std::string& username, const std::string& role) { const char* sql = "UPDATE users SET role = ? WHERE username = ?"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return false; } sqlite3_bind_text(stmt, 1, role.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 2, username.c_str(), -1, SQLITE_TRANSIENT); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc == SQLITE_DONE; } std::vector Database::searchUsers(const std::string& field, const std::string& value) { std::vector results; std::string sql = "SELECT id, username, password, salt, token, status, role, email, last_login, avatar_pic FROM users WHERE " + field + " LIKE ?"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql.c_str(), -1, &stmt, nullptr); if (rc != SQLITE_OK) { return results; } std::string search_pattern = "%" + value + "%"; sqlite3_bind_text(stmt, 1, search_pattern.c_str(), -1, SQLITE_TRANSIENT); while (sqlite3_step(stmt) == SQLITE_ROW) { UserRecord user; user.id = sqlite3_column_int(stmt, 0); user.username = reinterpret_cast(sqlite3_column_text(stmt, 1)); user.password_hash = reinterpret_cast(sqlite3_column_text(stmt, 2)); user.salt = reinterpret_cast(sqlite3_column_text(stmt, 3)); const char* token = reinterpret_cast(sqlite3_column_text(stmt, 4)); user.token = token ? token : ""; const char* status_str = reinterpret_cast(sqlite3_column_text(stmt, 5)); user.status = (status_str && std::string(status_str) == "online") ? UserStatus::ONLINE : UserStatus::OFFLINE; const char* role = reinterpret_cast(sqlite3_column_text(stmt, 6)); user.role = role ? role : ""; const char* email = reinterpret_cast(sqlite3_column_text(stmt, 7)); user.email = email ? email : ""; user.last_login = sqlite3_column_int64(stmt, 8); const void* blob = sqlite3_column_blob(stmt, 9); int blob_size = sqlite3_column_bytes(stmt, 9); if (blob && blob_size > 0) { const uint8_t* data = static_cast(blob); user.avatar_pic.assign(data, data + blob_size); } results.push_back(user); } sqlite3_finalize(stmt); return results; } std::vector Database::getAllUsers() { std::vector results; const char* sql = "SELECT id, username, password, salt, token, status, role, email, last_login, avatar_pic FROM users ORDER BY username"; sqlite3_stmt* stmt; int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, nullptr); if (rc != SQLITE_OK) { return results; } while (sqlite3_step(stmt) == SQLITE_ROW) { UserRecord user; user.id = sqlite3_column_int(stmt, 0); user.username = reinterpret_cast(sqlite3_column_text(stmt, 1)); user.password_hash = reinterpret_cast(sqlite3_column_text(stmt, 2)); user.salt = reinterpret_cast(sqlite3_column_text(stmt, 3)); const char* token = reinterpret_cast(sqlite3_column_text(stmt, 4)); user.token = token ? token : ""; const char* status_str = reinterpret_cast(sqlite3_column_text(stmt, 5)); user.status = (status_str && std::string(status_str) == "online") ? UserStatus::ONLINE : UserStatus::OFFLINE; const char* role = reinterpret_cast(sqlite3_column_text(stmt, 6)); user.role = role ? role : ""; const char* email = reinterpret_cast(sqlite3_column_text(stmt, 7)); user.email = email ? email : ""; user.last_login = sqlite3_column_int64(stmt, 8); const void* blob = sqlite3_column_blob(stmt, 9); int blob_size = sqlite3_column_bytes(stmt, 9); if (blob && blob_size > 0) { const uint8_t* data = static_cast(blob); user.avatar_pic.assign(data, data + blob_size); } results.push_back(user); } sqlite3_finalize(stmt); return results; } } // namespace scar