#include "PathManager.h" #include "ConfigEncryption.h" #include #include #include #include #include #include #include "VrLog.h" // 静态成员初始化 PathManager* PathManager::s_instance = nullptr; std::mutex PathManager::s_mutex; PathManager::PathManager(const QString& appName) : m_appName(appName) , m_encryptionEnabled(true) // 默认启用加密 { // 初始化时计算并缓存所有路径 m_configDirectory = GetConfigDirectory(); EnsureConfigDirectoryExists(); // 配置文件使用加密格式 m_configFilePath = m_configDirectory + "/config.encrypt"; m_calibrationFilePath = m_configDirectory + "/EyeHandCalibMatrixInfo.ini"; // 设置固定密钥:AppName + "VisionRobot" m_encryptionPassword = appName + "VisionRobot"; LOG_INFO("PathManager initialized for app: %s\n", appName.toStdString().c_str()); LOG_INFO("Config directory: %s\n", m_configDirectory.toStdString().c_str()); LOG_INFO("Encryption enabled with auto-generated key\n"); // 自动迁移旧的明文配置文件 MigrateToEncryptedConfig(); // 设置目录保护 SetDirectoryProtection(); } PathManager& PathManager::GetInstance() { std::lock_guard lock(s_mutex); if (s_instance == nullptr) { // 自动从应用程序路径获取应用名称 QString appFilePath = QCoreApplication::applicationFilePath(); QString appName = QFileInfo(appFilePath).baseName(); LOG_INFO("Auto-detected application name: %s\n", appName.toStdString().c_str()); s_instance = new PathManager(appName); } return *s_instance; } QString PathManager::GetConfigFilePath() const { return m_configFilePath; } QString PathManager::GetCalibrationFilePath() const { return m_calibrationFilePath; } QString PathManager::GetAppConfigDirectory() const { return m_configDirectory; } QString PathManager::GetConfigDirectory() const { QString baseDir; #ifdef _WIN32 // Windows系统:使用程序目录 baseDir = GetProgramDirectory(); #else // Linux系统:使用用户配置目录 baseDir = GetUserConfigDirectory(); #endif // 使用类成员变量中存储的应用名称,避免重复获取 return baseDir + "/../" + m_appName + "/Config"; } bool PathManager::EnsureConfigDirectoryExists() { if (QDir().exists(m_configDirectory)) { return true; } LOG_INFO("Creating configuration directory: %s\n", m_configDirectory.toStdString().c_str()); bool success = QDir().mkpath(m_configDirectory); if (success) { LOG_INFO("Configuration directory created successfully\n"); } else { LOG_ERROR("Failed to create configuration directory: %s\n", m_configDirectory.toStdString().c_str()); } return success; } QString PathManager::GetProgramDirectory() const { QString exePath = QCoreApplication::applicationFilePath(); return QFileInfo(exePath).absoluteDir().path(); } QString PathManager::GetUserConfigDirectory() const { return QStandardPaths::writableLocation(QStandardPaths::ConfigLocation); } void PathManager::SetEncryptionPassword(const QString& password) { std::lock_guard lock(s_mutex); m_encryptionPassword = password; LOG_INFO("配置加密密码已设置\n"); } bool PathManager::EnableEncryptionProtection(bool enable) { std::lock_guard lock(s_mutex); if (enable && m_encryptionPassword.isEmpty()) { LOG_ERROR("启用加密保护失败:未设置加密密码\n"); return false; } m_encryptionEnabled = enable; if (enable) { LOG_INFO("配置加密保护已启用\n"); // 设置目录权限 if (!SetDirectoryProtection()) { LOG_WARN("设置目录权限失败,但加密功能仍然启用\n"); } } else { LOG_INFO("配置加密保护已禁用\n"); } return true; } QByteArray PathManager::ReadEncryptedConfig(const QString& filePath) const { if (!m_encryptionEnabled) { // 未启用加密,直接读取文件 QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { LOG_ERROR("无法打开配置文件: %s\n", filePath.toStdString().c_str()); return QByteArray(); } return file.readAll(); } // 启用了加密,读取并解密 QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { LOG_ERROR("无法打开配置文件: %s\n", filePath.toStdString().c_str()); return QByteArray(); } QByteArray encryptedData = file.readAll(); file.close(); if (encryptedData.isEmpty()) { LOG_ERROR("配置文件为空: %s\n", filePath.toStdString().c_str()); return QByteArray(); } // 检查是否为加密文件 if (!IsConfigEncrypted(filePath)) { // 文件未加密,直接返回 LOG_INFO("配置文件未加密,直接返回: %s\n", filePath.toStdString().c_str()); return encryptedData; } // 解密文件 QByteArray decryptedData = ConfigEncryption::DecryptConfig(encryptedData, m_encryptionPassword); if (decryptedData.isEmpty()) { LOG_ERROR("解密配置文件失败: %s(可能是密码错误)\n", filePath.toStdString().c_str()); } return decryptedData; } bool PathManager::WriteEncryptedConfig(const QString& filePath, const QByteArray& data) { if (data.isEmpty()) { LOG_ERROR("写入配置文件失败:数据为空\n"); return false; } if (!m_encryptionEnabled) { // 未启用加密,直接写入文件 QFile file(filePath); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { LOG_ERROR("无法打开配置文件进行写入: %s\n", filePath.toStdString().c_str()); return false; } qint64 written = file.write(data); file.close(); if (written != data.size()) { LOG_ERROR("写入配置文件失败: %s\n", filePath.toStdString().c_str()); return false; } LOG_INFO("配置文件写入成功(未加密): %s\n", filePath.toStdString().c_str()); return true; } // 启用了加密,加密后写入 QByteArray encryptedData = ConfigEncryption::EncryptConfig(data, m_encryptionPassword); if (encryptedData.isEmpty()) { LOG_ERROR("加密配置数据失败\n"); return false; } QFile file(filePath); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { LOG_ERROR("无法打开配置文件进行写入: %s\n", filePath.toStdString().c_str()); return false; } qint64 written = file.write(encryptedData); file.close(); if (written != encryptedData.size()) { LOG_ERROR("写入加密配置文件失败: %s\n", filePath.toStdString().c_str()); return false; } LOG_INFO("加密配置文件写入成功: %s\n", filePath.toStdString().c_str()); return true; } bool PathManager::IsConfigEncrypted(const QString& filePath) const { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { return false; } // 读取文件头部,检查是否有加密标记 QByteArray header = file.read(8); file.close(); return header == QByteArray("VRENC1.0", 8); } bool PathManager::SetDirectoryProtection() { return ConfigEncryption::SetDirectoryPermissions(m_configDirectory); } QString PathManager::GetEncryptionKey() const { return m_encryptionPassword; } void PathManager::MigrateToEncryptedConfig() { QString oldConfigPath = m_configDirectory + "/config.xml"; QString newConfigPath = m_configFilePath; // config.encrypt QFile oldFile(oldConfigPath); QFile newFile(newConfigPath); // 检查是否需要迁移:config.xml 存在但 config.encrypt 不存在 if (oldFile.exists() && !newFile.exists()) { LOG_INFO("发现旧的明文配置文件,开始迁移到加密格式...\n"); // 读取旧配置文件 if (!oldFile.open(QIODevice::ReadOnly)) { LOG_ERROR("无法打开旧配置文件进行迁移: %s\n", oldConfigPath.toStdString().c_str()); return; } QByteArray plainData = oldFile.readAll(); oldFile.close(); if (plainData.isEmpty()) { LOG_WARN("旧配置文件为空,跳过迁移\n"); return; } // 加密并保存到新文件 if (WriteEncryptedConfig(newConfigPath, plainData)) { LOG_INFO("配置文件已成功加密并保存到: %s\n", newConfigPath.toStdString().c_str()); // 删除旧的明文文件 if (oldFile.remove()) { LOG_INFO("已删除旧的明文配置文件: %s\n", oldConfigPath.toStdString().c_str()); } else { LOG_WARN("删除旧配置文件失败: %s\n", oldConfigPath.toStdString().c_str()); } } else { LOG_ERROR("加密配置文件失败,保留旧文件\n"); } } else if (oldFile.exists() && newFile.exists()) { // 两个文件都存在,说明用户可能刚解密过,准备重新加密 LOG_INFO("检测到 config.xml 和 config.encrypt 同时存在\n"); LOG_INFO("将使用 config.xml 更新 config.encrypt 并删除 config.xml\n"); // 读取 config.xml if (!oldFile.open(QIODevice::ReadOnly)) { LOG_ERROR("无法打开 config.xml: %s\n", oldConfigPath.toStdString().c_str()); return; } QByteArray plainData = oldFile.readAll(); oldFile.close(); if (!plainData.isEmpty()) { // 更新加密文件 if (WriteEncryptedConfig(newConfigPath, plainData)) { LOG_INFO("已使用 config.xml 更新 config.encrypt\n"); // 删除 config.xml if (oldFile.remove()) { LOG_INFO("已删除 config.xml\n"); } else { LOG_WARN("删除 config.xml 失败: %s\n", oldConfigPath.toStdString().c_str()); } } else { LOG_ERROR("更新加密配置文件失败\n"); } } } else { LOG_INFO("配置文件已是加密格式,无需迁移\n"); } }