#include "db_manager.h" #include "../shared/crypto/argon2_wrapper.h" #include #include #include #include #include namespace scar { DBManager::DBManager(const std::string& db_path) { db_ = std::make_unique(db_path); if (!db_->initialize()) { throw std::runtime_error("Failed to initialize database: " + db_path); } } bool DBManager::addUser(const std::string& username, const std::string& password, const std::string& avatar) { // Check if user already exists auto existing_user = db_->getUserByUsername(username); if (existing_user) { std::cerr << "Error: User '" << username << "' already exists" << std::endl; return false; } // Generate random salt std::string salt = Argon2Wrapper::generateSalt(); // Hash password with salt std::string password_hash = Argon2Wrapper::hashPassword(password, salt); // Create user if (!db_->createUser(username, password_hash, salt)) { std::cerr << "Error: Failed to create user" << std::endl; return false; } // Set avatar if provided if (!avatar.empty()) { modifyAvatar(username, avatar); } std::cout << "User '" << username << "' created successfully" << std::endl; std::cout << " Salt: " << salt << std::endl; std::cout << " Hash: " << password_hash.substr(0, 32) << "..." << std::endl; return true; } bool DBManager::deleteUser(const std::string& username) { auto user = db_->getUserByUsername(username); if (!user) { std::cerr << "Error: User '" << username << "' not found" << std::endl; return false; } // Execute delete if (!db_->deleteUser(username)) { std::cerr << "Error: Failed to delete user" << std::endl; return false; } std::cout << "User '" << username << "' deleted successfully" << std::endl; return true; } bool DBManager::modifyPassword(const std::string& username, const std::string& new_password) { auto user = db_->getUserByUsername(username); if (!user) { std::cerr << "Error: User '" << username << "' not found" << std::endl; return false; } // Generate new salt std::string salt = Argon2Wrapper::generateSalt(); // Hash new password std::string password_hash = Argon2Wrapper::hashPassword(new_password, salt); // Update database if (!db_->updateUserPassword(username, password_hash, salt)) { std::cerr << "Error: Failed to update password" << std::endl; return false; } std::cout << "Password updated for user '" << username << "'" << std::endl; return true; } bool DBManager::modifyAvatar(const std::string& username, const std::string& avatar_path) { auto user = db_->getUserByUsername(username); if (!user) { std::cerr << "Error: User '" << username << "' not found" << std::endl; return false; } // Read avatar file auto avatar_data = readAvatarFile(avatar_path); if (avatar_data.empty()) { std::cerr << "Error: Failed to read avatar file" << std::endl; return false; } // Update database if (!db_->updateUserAvatar(username, avatar_data)) { std::cerr << "Error: Failed to update avatar" << std::endl; return false; } std::cout << "Avatar updated for user '" << username << "' (" << avatar_data.size() << " bytes)" << std::endl; return true; } bool DBManager::modifyEmail(const std::string& username, const std::string& email) { auto user = db_->getUserByUsername(username); if (!user) { std::cerr << "Error: User '" << username << "' not found" << std::endl; return false; } if (!db_->updateUserEmail(username, email)) { std::cerr << "Error: Failed to update email" << std::endl; return false; } std::cout << "Email updated for user '" << username << "'" << std::endl; return true; } bool DBManager::modifyRole(const std::string& username, const std::string& role) { auto user = db_->getUserByUsername(username); if (!user) { std::cerr << "Error: User '" << username << "' not found" << std::endl; return false; } if (!db_->updateUserRole(username, role)) { std::cerr << "Error: Failed to update role" << std::endl; return false; } std::cout << "Role updated for user '" << username << "'" << std::endl; return true; } void DBManager::fetchUser(const std::string& username) { auto user = db_->getUserByUsername(username); if (!user) { std::cerr << "User '" << username << "' not found" << std::endl; return; } printUserDetails(*user); } void DBManager::searchUsers(const std::string& field, const std::string& value) { auto users = db_->searchUsers(field, value); if (users.empty()) { std::cout << "No users found matching " << field << "='" << value << "'" << std::endl; return; } std::cout << "Found " << users.size() << " user(s):" << std::endl; std::cout << std::string(80, '-') << std::endl; for (const auto& user : users) { printUserDetails(user); std::cout << std::string(80, '-') << std::endl; } } void DBManager::listAllUsers() { auto users = db_->getAllUsers(); if (users.empty()) { std::cout << "No users in database" << std::endl; return; } std::cout << "Total users: " << users.size() << std::endl; std::cout << std::string(100, '=') << std::endl; // Table header std::cout << std::left << std::setw(20) << "Username" << std::setw(10) << "Status" << std::setw(25) << "Email" << std::setw(15) << "Role" << std::setw(20) << "Last Login" << std::endl; std::cout << std::string(100, '-') << std::endl; for (const auto& user : users) { std::string status = (user.status == UserStatus::ONLINE) ? "Online" : "Offline"; std::string last_login = "Never"; if (user.last_login > 0) { std::time_t t = static_cast(user.last_login); std::tm* tm = std::localtime(&t); char buffer[32]; std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm); last_login = buffer; } std::cout << std::left << std::setw(20) << user.username << std::setw(10) << status << std::setw(25) << user.email << std::setw(15) << user.role << std::setw(20) << last_login << std::endl; } } std::string DBManager::findDatabase(const std::string& override_path) { // If path provided, use it if (!override_path.empty()) { if (std::filesystem::exists(override_path)) { return override_path; } std::cerr << "Warning: Specified database not found: " << override_path << std::endl; } // Check current directory if (std::filesystem::exists("scarchat.db")) { return "scarchat.db"; } // Check install path (would be set by CMake install) std::string install_path = "/usr/local/share/scarchat/scarchat.db"; if (std::filesystem::exists(install_path)) { return install_path; } // Check user's home directory const char* home = std::getenv("HOME"); if (home) { std::string home_path = std::string(home) + "/.local/share/scarchat/scarchat.db"; if (std::filesystem::exists(home_path)) { return home_path; } } // Default to current directory (will be created if doesn't exist) return "scarchat.db"; } void DBManager::printUserDetails(const UserRecord& user) { std::cout << "User ID: " << user.id << std::endl; std::cout << "Username: " << user.username << std::endl; std::cout << "Status: " << (user.status == UserStatus::ONLINE ? "Online" : "Offline") << std::endl; std::cout << "Email: " << (user.email.empty() ? "(not set)" : user.email) << std::endl; std::cout << "Role: " << (user.role.empty() ? "(not set)" : user.role) << std::endl; if (user.last_login > 0) { std::time_t t = static_cast(user.last_login); std::cout << "Last Login: " << std::ctime(&t); } else { std::cout << "Last Login: Never" << std::endl; } std::cout << "Has Avatar: " << (user.avatar_pic.empty() ? "No" : "Yes") << std::endl; std::cout << "Token: " << (user.token.empty() ? "(none)" : user.token.substr(0, 20) + "...") << std::endl; std::cout << "Salt: " << user.salt << std::endl; std::cout << "Password Hash: " << user.password_hash.substr(0, 32) << "..." << std::endl; } std::vector DBManager::readAvatarFile(const std::string& path) { std::ifstream file(path, std::ios::binary | std::ios::ate); if (!file.is_open()) { return {}; } std::streamsize size = file.tellg(); file.seekg(0, std::ios::beg); std::vector buffer(size); if (!file.read(reinterpret_cast(buffer.data()), size)) { return {}; } return buffer; } } // namespace scar