GrabBag/AppUtils/AppCommon/Src/PathManager.cpp

342 lines
11 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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");
}
}