342 lines
11 KiB
C++
342 lines
11 KiB
C++
#include "PathManager.h"
|
||
#include "ConfigEncryption.h"
|
||
#include <QtCore/QCoreApplication>
|
||
#include <QtCore/QFileInfo>
|
||
#include <QtCore/QDir>
|
||
#include <QtCore/QStandardPaths>
|
||
#include <QtCore/QFile>
|
||
#include <stdexcept>
|
||
#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<std::mutex> 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<std::mutex> lock(s_mutex);
|
||
m_encryptionPassword = password;
|
||
LOG_INFO("配置加密密码已设置\n");
|
||
}
|
||
|
||
bool PathManager::EnableEncryptionProtection(bool enable)
|
||
{
|
||
std::lock_guard<std::mutex> 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");
|
||
}
|
||
}
|