diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f9993c..ddf13ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,7 @@ set(PROJECT_SOURCES src/global/Configs.cpp src/global/Utils.cpp src/global/HTTPRequestHelper.cpp + src/global/DeviceDetailsHelper.cpp 3rdparty/base64.cpp 3rdparty/qrcodegen.cpp diff --git a/include/global/DataStore.hpp b/include/global/DataStore.hpp index 3d0cd31..a72d962 100644 --- a/include/global/DataStore.hpp +++ b/include/global/DataStore.hpp @@ -95,6 +95,7 @@ namespace Configs { bool sub_clear = false; bool sub_insecure = false; int sub_auto_update = -30; + bool sub_send_hwid = false; // Security bool skip_cert = false; diff --git a/include/global/DeviceDetailsHelper.hpp b/include/global/DeviceDetailsHelper.hpp new file mode 100644 index 0000000..0cfe289 --- /dev/null +++ b/include/global/DeviceDetailsHelper.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +struct DeviceDetails { + QString hwid; + QString os; + QString osVersion; + QString model; +}; + +DeviceDetails GetDeviceDetails(); \ No newline at end of file diff --git a/include/ui/setting/dialog_basic_settings.ui b/include/ui/setting/dialog_basic_settings.ui index bb06225..c1e2be3 100644 --- a/include/ui/setting/dialog_basic_settings.ui +++ b/include/ui/setting/dialog_basic_settings.ui @@ -551,6 +551,16 @@ + + + + <html><head/><body><p>HWID=%1</p><p>OS=%2</p><p>OS Version=%3</p><p>Model=%4</p></body></html> + + + Enable sending HWID, device model, and OS version when updating subscription + + + diff --git a/res/translations/ru_RU.ts b/res/translations/ru_RU.ts index a17db29..03e7aee 100644 --- a/res/translations/ru_RU.ts +++ b/res/translations/ru_RU.ts @@ -90,6 +90,14 @@ Clear servers before updating subscription Очищать список серверов перед обновлением подписки + + + Enable sending HWID, device model, and OS version when updating subscription + Включить отправку HWID, модели устройства и версии ОС при обновлении подписки + + + <html><head/><body><p>HWID=%1</p><p>OS=%2</p><p>OS Version=%3</p><p>Model=%4</p></body></html> + <html><head/><body><p>HWID=%1</p><p>ОС=%2</p><p>Версия ОС=%3</p><p>Модель=%4</p></body></html> Core diff --git a/src/global/Configs.cpp b/src/global/Configs.cpp index ad509ce..1a41674 100644 --- a/src/global/Configs.cpp +++ b/src/global/Configs.cpp @@ -279,6 +279,7 @@ namespace Configs { _add(new configItem("sub_clear", &sub_clear, itemType::boolean)); _add(new configItem("sub_insecure", &sub_insecure, itemType::boolean)); _add(new configItem("sub_auto_update", &sub_auto_update, itemType::integer)); + _add(new configItem("sub_send_hwid", &sub_send_hwid, itemType::boolean)); _add(new configItem("start_minimal", &start_minimal, itemType::boolean)); _add(new configItem("max_log_line", &max_log_line, itemType::integer)); _add(new configItem("splitter_state", &splitter_state, itemType::string)); diff --git a/src/global/DeviceDetailsHelper.cpp b/src/global/DeviceDetailsHelper.cpp new file mode 100644 index 0000000..cf66ffb --- /dev/null +++ b/src/global/DeviceDetailsHelper.cpp @@ -0,0 +1,183 @@ +#include "include/global/DeviceDetailsHelper.hpp" + +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +#include +#include "include/sys/windows/WinVersion.h" +#include +#include +#pragma comment(lib, "wbemuuid.lib") +#endif + +#ifdef Q_OS_WIN +static QString queryWmiProperty(const QString& wmiClass, const QString& property) { + HRESULT hres; + + hres = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hres)) return QString(); + + hres = CoInitializeSecurity( + NULL, -1, NULL, NULL, + RPC_C_AUTHN_LEVEL_DEFAULT, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, EOAC_NONE, NULL + ); + if (FAILED(hres) && hres != RPC_E_TOO_LATE) { + CoUninitialize(); + return QString(); + } + + IWbemLocator* pLoc = NULL; + hres = CoCreateInstance( + CLSID_WbemLocator, 0, + CLSCTX_INPROC_SERVER, + IID_IWbemLocator, (LPVOID*)&pLoc + ); + if (FAILED(hres)) { + CoUninitialize(); + return QString(); + } + + IWbemServices* pSvc = NULL; + BSTR bstrNamespace = SysAllocString(L"ROOT\\CIMV2"); + hres = pLoc->ConnectServer( + bstrNamespace, + NULL, NULL, NULL, 0, NULL, 0, &pSvc + ); + SysFreeString(bstrNamespace); + if (FAILED(hres)) { + pLoc->Release(); + CoUninitialize(); + return QString(); + } + + hres = CoSetProxyBlanket( + pSvc, + RPC_C_AUTHN_WINNT, + RPC_C_AUTHZ_NONE, + NULL, + RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE + ); + if (FAILED(hres)) { + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return QString(); + } + + IEnumWbemClassObject* pEnumerator = NULL; + QString query = QString("SELECT %1 FROM %2").arg(property, wmiClass); + BSTR bstrWQL = SysAllocString(L"WQL"); + BSTR bstrQuery = SysAllocString(query.toStdWString().c_str()); + hres = pSvc->ExecQuery( + bstrWQL, + bstrQuery, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, + &pEnumerator + ); + SysFreeString(bstrWQL); + SysFreeString(bstrQuery); + if (FAILED(hres)) { + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return QString(); + } + + IWbemClassObject* pclsObj = NULL; + ULONG uReturn = 0; + QString result; + + if (pEnumerator) { + HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); + if (uReturn) { + VARIANT vtProp; + VariantInit(&vtProp); + hr = pclsObj->Get(property.toStdWString().c_str(), 0, &vtProp, 0, 0); + if (SUCCEEDED(hr) && vtProp.vt == VT_BSTR) { + result = QString::fromWCharArray(vtProp.bstrVal); + } + VariantClear(&vtProp); + pclsObj->Release(); + } + pEnumerator->Release(); + } + + pSvc->Release(); + pLoc->Release(); + CoUninitialize(); + return result; +} + +static QString winBaseBoard() { + return queryWmiProperty("Win32_BaseBoard", "Product"); +} + +static QString winModel() { + return queryWmiProperty("Win32_ComputerSystem", "Model"); +} +#endif + +DeviceDetails GetDeviceDetails() { + static const DeviceDetails details = []() { + DeviceDetails d; + + #ifdef Q_OS_WIN + d.hwid = QSysInfo::machineUniqueId(); + if (d.hwid.isEmpty()) { + auto productType = QSysInfo::productType().toUtf8(); + d.hwid = QString("%1-%2").arg(QSysInfo::machineHostName(), QString::fromUtf8(productType)); + } + + d.os = QStringLiteral("Windows"); + + VersionInfo info; + WinVersion::GetVersion(info); + d.osVersion = QString("%1.%2.%3").arg(info.Major).arg(info.Minor).arg(info.BuildNum); + + auto wm = winModel(); + auto wbb = winBaseBoard(); + d.model = (wm == wbb) ? wm : wm + "/" + wbb; + if (d.hwid.isEmpty()) d.model = QSysInfo::prettyProductName(); + #elif defined(Q_OS_LINUX) + QString mid; + QFile f1("/etc/machine-id"); + if (f1.exists() && f1.open(QIODevice::ReadOnly | QIODevice::Text)) { + mid = QString::fromUtf8(f1.readAll()).trimmed(); + f1.close(); + } + else { + QFile f2("/var/lib/dbus/machine-id"); + if (f2.exists() && f2.open(QIODevice::ReadOnly | QIODevice::Text)) { + mid = QString::fromUtf8(f2.readAll()).trimmed(); + f2.close(); + } + } + d.hwid = mid; + d.os = QStringLiteral("Linux"); + d.osVersion = QSysInfo::kernelVersion(); + d.model = QSysInfo::prettyProductName(); + #elif defined(Q_OS_MACOS) + d.hwid = QSysInfo::machineUniqueId(); + d.os = QStringLiteral("macOS"); + d.osVersion = QSysInfo::productVersion(); + d.model = QSysInfo::prettyProductName(); + #else + d.hwid = QSysInfo::machineUniqueId(); + d.os = QSysInfo::productType(); + d.osVersion = QSysInfo::productVersion(); + d.model = QSysInfo::prettyProductName(); + #endif + return d; + }(); + return details; +} \ No newline at end of file diff --git a/src/global/HTTPRequestHelper.cpp b/src/global/HTTPRequestHelper.cpp index 1f7ff81..01fd208 100644 --- a/src/global/HTTPRequestHelper.cpp +++ b/src/global/HTTPRequestHelper.cpp @@ -10,6 +10,7 @@ #include "include/global/Configs.hpp" #include "include/ui/mainwindow.h" +#include "include/global/DeviceDetailsHelper.hpp" namespace Configs_network { @@ -35,6 +36,15 @@ namespace Configs_network { c.setPeerVerifyMode(QSslSocket::PeerVerifyMode::VerifyNone); request.setSslConfiguration(c); } + //Attach HWID and device info headers if enabled in settings + if (Configs::dataStore->sub_send_hwid && !request.url().toString().contains("/throneproj/")) { + auto details = GetDeviceDetails(); + + if (!details.hwid.isEmpty()) request.setRawHeader("x-hwid", details.hwid.toUtf8()); + if (!details.os.isEmpty()) request.setRawHeader("x-device-os", details.os.toUtf8()); + if (!details.osVersion.isEmpty()) request.setRawHeader("x-ver-os", details.osVersion.toUtf8()); + if (!details.model.isEmpty()) request.setRawHeader("x-device-model", details.model.toUtf8()); + } // auto _reply = accessManager.get(request); connect(_reply, &QNetworkReply::sslErrors, _reply, [](const QList &errors) { diff --git a/src/ui/mainwindow.cpp b/src/ui/mainwindow.cpp index fdca3cc..61828af 100644 --- a/src/ui/mainwindow.cpp +++ b/src/ui/mainwindow.cpp @@ -54,6 +54,7 @@ #include <3rdparty/QHotkey/qhotkey.h> #include <3rdparty/qv2ray/v2/proxy/QvProxyConfigurator.hpp> #include +#include "include/global/DeviceDetailsHelper.hpp" #include "include/sys/macos/MacOS.h" @@ -133,6 +134,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi Configs::dataStore->inbound_socks_port = MkPort(); } + //init HWID data + runOnNewThread([=, this] {GetDeviceDetails(); }); + // Prepare core Configs::dataStore->core_port = MkPort(); if (Configs::dataStore->core_port <= 0) Configs::dataStore->core_port = 19810; diff --git a/src/ui/setting/dialog_basic_settings.cpp b/src/ui/setting/dialog_basic_settings.cpp index 76c2d6c..a6dd412 100644 --- a/src/ui/setting/dialog_basic_settings.cpp +++ b/src/ui/setting/dialog_basic_settings.cpp @@ -7,6 +7,7 @@ #include "include/global/GuiUtils.hpp" #include "include/global/Configs.hpp" #include "include/global/HTTPRequestHelper.hpp" +#include "include/global/DeviceDetailsHelper.hpp" #include #include @@ -116,7 +117,15 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent) D_LOAD_BOOL(sub_use_proxy) D_LOAD_BOOL(sub_clear) D_LOAD_BOOL(sub_insecure) + D_LOAD_BOOL(sub_send_hwid) D_LOAD_INT_ENABLE(sub_auto_update, sub_auto_update_enable) + auto details = GetDeviceDetails(); + ui->sub_send_hwid->setToolTip( + ui->sub_send_hwid->toolTip() + .arg(details.hwid.isEmpty() ? "N/A" : details.hwid, + details.os.isEmpty() ? "N/A" : details.os, + details.osVersion.isEmpty() ? "N/A" : details.osVersion, + details.model.isEmpty() ? "N/A" : details.model)); // Core ui->groupBox_core->setTitle(software_core_name); @@ -200,6 +209,7 @@ void DialogBasicSettings::accept() { D_SAVE_BOOL(sub_use_proxy) D_SAVE_BOOL(sub_clear) D_SAVE_BOOL(sub_insecure) + D_SAVE_BOOL(sub_send_hwid) D_SAVE_INT_ENABLE(sub_auto_update, sub_auto_update_enable) // Core