chore: initialize client repository

This commit is contained in:
2026-07-09 09:17:30 +00:00
commit b06e003502
44 changed files with 7225 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
project(MainApp)
add_executable(MainApp
main.cpp
MainWindow.h
MainWindow.cpp
../Common/ConfigHelper.h
)
target_include_directories(MainApp
PUBLIC ${CMAKE_SOURCE_DIR}/Common
PRIVATE ${OPENSSL_INC}
)
target_link_libraries(MainApp PRIVATE Common Qt5::Core Qt5::Gui Qt5::Widgets)
if(WIN32)
get_target_property(QT_WINDEPLOYQT_EXE Qt5::qmake IMPORTED_LOCATION)
get_filename_component(QT_BIN_PATH "${QT_WINDEPLOYQT_EXE}" DIRECTORY)
set(WINDEPLOYQT "${QT_BIN_PATH}/windeployqt.exe")
add_custom_command(TARGET MainApp POST_BUILD
COMMAND ${WINDEPLOYQT} $<IF:$<CONFIG:Debug>,--debug,--release> $<TARGET_FILE:MainApp>
)
endif()
+72
View File
@@ -0,0 +1,72 @@
#include "MainWindow.h"
#include <QApplication>
#include <QFont>
#include <QFile>
#include <QLabel>
#include <QVBoxLayout>
#include <QCryptographicHash>
#include "../Common/PolicyHelper.h"
#include "../Common/ConfigHelper.h"
static QString readDllVersion(const QString& dllPath)
{
QFile file(dllPath);
if (!file.exists())
return "missing";
if (!file.open(QIODevice::ReadOnly))
return "unreadable";
QByteArray data = file.read(1024);
file.close();
return QString::fromUtf8(QCryptographicHash::hash(data, QCryptographicHash::Sha256).toHex().left(8));
}
MainWindow::MainWindow(QWidget* parent)
: QWidget(parent)
{
this->setWindowTitle("Marsco Demo MainApp");
this->resize(600, 400);
QString appVersion = ConfigHelper::instance().getValue("App", "current_version");
if (appVersion.isEmpty())
appVersion = "unknown";
QString dllVersion = readDllVersion(QApplication::applicationDirPath() + "/plugins/demo_plugin.dll");
PolicyHelper policy(QApplication::applicationDirPath());
QString policyResult;
if (!policy.loadPolicy("config/version_policy.dat"))
{
policyResult = "policy missing";
}
else if (!policy.isValid())
{
policyResult = "policy invalid";
}
else if (!policy.isVersionAllowed(appVersion))
{
policyResult = "version disabled";
}
else if (policy.isExpired())
{
policyResult = "policy expired";
}
else
{
policyResult = "policy ok";
}
QVBoxLayout* layout = new QVBoxLayout(this);
QLabel* label = new QLabel(QString("Software Running Successfully\nVersion: %1\nDLL Version: %2\nPolicy: %3")
.arg(appVersion)
.arg(dllVersion)
.arg(policyResult));
QFont font = label->font();
font.setPointSize(14);
label->setFont(font);
label->setAlignment(Qt::AlignCenter);
layout->addWidget(label);
this->setLayout(layout);
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>
class MainWindow : public QWidget
{
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
};
+130
View File
@@ -0,0 +1,130 @@
#include <Windows.h>
#include <QApplication>
#include <QDebug>
#include <QTextCodec>
#include <QMessageBox>
#include <QDir>
#include <QFileInfo>
#include <QSaveFile>
#include "MainWindow.h"
#include "../Common/ConfigHelper.h"
#include "../Common/PolicyHelper.h"
#include "../Common/LocalStateHelper.h"
#include "../Common/TicketHelper.h"
#include "../Common/IntegrityHelper.h"
#include "../Common/DeviceIdentityHelper.h"
int main(int argc, char* argv[])
{
SetConsoleOutputCP(936);
QTextCodec::setCodecForLocale(QTextCodec::codecForName("GBK"));
QApplication a(argc, argv);
qDebug() << Qt::endl << "entered main app" << Qt::endl;
ConfigHelper& config = ConfigHelper::instance();
QString launcherExecutable = config.getValue("Runtime", "launcher_executable").trimmed();
if (launcherExecutable.isEmpty()) launcherExecutable = "Launcher.exe";
QString ticketFilePath;
QString healthFilePath;
for (int i = 1; i < argc; ++i)
{
const QString arg(argv[i]);
if (arg.startsWith("--ticket-file="))
ticketFilePath = arg.mid(QString("--ticket-file=").size());
else if (arg.startsWith("--health-file="))
healthFilePath = arg.mid(QString("--health-file=").size());
}
QString ticketError;
if (ticketFilePath.isEmpty()
|| !TicketHelper::consumeAndVerify(ticketFilePath,
config.getValue("App", "app_id"), config.getValue("Update", "device_id"),
config.getValue("App", "current_version"), config.getValue("App", "launch_token"),
&ticketError))
{
QMessageBox::critical(nullptr, "Startup Restriction",
QString("Invalid or missing one-time launch ticket: %1\nPlease use %2.")
.arg(ticketError, launcherExecutable));
return -1;
}
QString appDir = QApplication::applicationDirPath();
const QString installRoot = config.installRoot();
DeviceIdentityHelper identity(appDir);
if (!identity.verifyLocal(config.getValue("App", "app_id"), config.getValue("App", "channel")))
{
QMessageBox::critical(nullptr, "License Error", QString("Local license invalid: %1").arg(identity.errorString()));
return -1;
}
PolicyHelper policy(appDir);
if (!policy.loadPolicy("config/version_policy.dat") || !policy.isValid())
{
QMessageBox::critical(nullptr, "Policy Error", QString("Local policy invalid: %1").arg(policy.errorString()));
return -1;
}
if (policy.isExpired())
{
QMessageBox::critical(nullptr, "Policy Error", "Policy expired, cannot start the application.");
return -1;
}
LocalStateHelper state(appDir);
if (!state.loadState())
{
QMessageBox::critical(nullptr, "State Error", QString("Cannot load local state: %1").arg(state.errorString()));
return -1;
}
if (state.isPolicySeqRolledBack(policy.policySeq()))
{
QMessageBox::critical(nullptr, "Policy Error", "Detected policy sequence rollback, startup blocked.");
return -1;
}
if (state.isSystemTimeRewound())
{
QMessageBox::critical(nullptr, "Policy Error", "System time appears to be rewound, startup blocked.");
return -1;
}
IntegrityHelper integrity(installRoot);
if (!integrity.verifyInstalledVersion(
config.getValue("App", "app_id"), config.getValue("App", "channel"),
config.getValue("App", "current_version")))
{
QMessageBox::critical(nullptr, "Integrity Check Failed",
QString("Application files failed signed Manifest verification:\n%1")
.arg(integrity.errorString()));
return -1;
}
MainWindow w;
w.show();
state.updateAfterSuccessfulRun(ConfigHelper::instance().getValue("App", "current_version"), policy.policySeq());
if (!state.saveState())
{
QMessageBox::critical(nullptr, "State Error", QString("Cannot save local state: %1").arg(state.errorString()));
return -1;
}
if (!healthFilePath.isEmpty())
{
QDir().mkpath(QFileInfo(healthFilePath).path());
QSaveFile healthFile(healthFilePath);
const QByteArray healthy("ok\n");
if (!healthFile.open(QIODevice::WriteOnly)
|| healthFile.write(healthy) != healthy.size()
|| !healthFile.commit())
{
QMessageBox::critical(nullptr, "Startup Error", "Cannot write update health confirmation file.");
return -1;
}
}
qDebug() << "Main program MainApp is running normally";
return a.exec();
}