From b72a93d0f9df93b1b9f64c2b3b69a7b20f0ae605 Mon Sep 17 00:00:00 2001 From: jiadong Date: Wed, 11 Feb 2026 17:26:04 +0800 Subject: [PATCH 1/3] =?UTF-8?q?[200=5F48]=20=E6=B7=BB=E5=8A=A0=20User-Agen?= =?UTF-8?q?t=20and=20Device=20ID=20=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- devel/200_48.md | 18 +++++ src/Plugins/Qt/qt_sys_utils.cpp | 113 ++++++++++++++++++++++++++++++- src/Plugins/Qt/qt_sys_utils.hpp | 2 + src/Scheme/Glue/glue_basic.lua | 10 +++ src/System/Misc/tm_sys_utils.cpp | 22 ++++++ src/System/Misc/tm_sys_utils.hpp | 3 + 6 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 devel/200_48.md diff --git a/devel/200_48.md b/devel/200_48.md new file mode 100644 index 0000000000..73e35a8c62 --- /dev/null +++ b/devel/200_48.md @@ -0,0 +1,18 @@ +# [200_48] User-Agent and Device ID + +## 2026/02/11 添加 User-Agent and Device ID 函数 + +### What +提供 HTTP 请求所需的 User-Agent 和设备唯一标识符功能。 +- **User-Agent**: 在 HTTP 请求头中携带客户端信息(应用版本、操作系统、架构),便于服务器识别客户端环境 + ``` + LiiiSTEM-v2026.2.1 Windows_11_10.0.26100 x86_64 + LiiiSTEM-v2026.2.1 macOS_15.3_(arm64) arm64 + LiiiSTEM-v2026.2.1 Debian_GNU/Linux_13_(trixie) x86_64 + ``` +- **Device ID**: 基于网卡 MAC 地址生成的设备唯一标识符,用于设备认证、防止多设备共享账户等场景 + +### Why +- HTTP 请求时准确标识客户端环境,便于服务端做兼容性处理 +- 设备绑定和认证,防止账户在多设备上滥用 +- 统一各平台的系统信息获取方式,提供更详细的版本信息(如 Windows 11 26200.7623) diff --git a/src/Plugins/Qt/qt_sys_utils.cpp b/src/Plugins/Qt/qt_sys_utils.cpp index 3c4c7f316b..360f7cbb26 100644 --- a/src/Plugins/Qt/qt_sys_utils.cpp +++ b/src/Plugins/Qt/qt_sys_utils.cpp @@ -1,4 +1,3 @@ - /****************************************************************************** * MODULE : qt_sys_utils.cpp * DESCRIPTION: external command launcher @@ -14,14 +13,23 @@ #include "file.hpp" #include "qt_utilities.hpp" #include "string.hpp" +#include "tm_configure.hpp" #include "tm_debug.hpp" #include +#include +#include +#include #include #include #include #include +#ifdef Q_OS_WINDOWS +#include +#include +#endif + string qt_get_current_cpu_arch () { return from_qstring (QSysInfo::currentCpuArchitecture ()); @@ -32,6 +40,109 @@ qt_get_pretty_os_name () { return from_qstring (QSysInfo::prettyProductName ()); } +#ifdef Q_OS_WINDOWS +QString +get_windows_detailed_version() { + RTL_OSVERSIONINFOW osvi; + osvi.dwOSVersionInfoSize = sizeof(osvi); + if (RtlGetVersion(&osvi) != 0) { + return QSysInfo::prettyProductName(); + } + + QString productName; + if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0) { + if (osvi.dwBuildNumber >= 22000) productName = "Windows 11"; + else productName = "Windows 10"; + } else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) { + productName = "Windows 8.1"; + } else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) { + productName = "Windows 8"; + } else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) { + productName = "Windows 7"; + } else { + productName = QString("Windows %1.%2").arg(osvi.dwMajorVersion).arg(osvi.dwMinorVersion); + } + + return QString("%1 %2.%3.%4").arg(productName).arg(osvi.dwMajorVersion).arg(osvi.dwMinorVersion).arg(osvi.dwBuildNumber).replace(" ", "_"); +} +#endif + +#ifdef Q_OS_MACOS +QString +get_macos_detailed_version() { + QProcess p; + QStringList env = QProcess::systemEnvironment(); + p.setEnvironment(env); + p.start("sh", QStringList() << "-c" << "sw_vers -productVersion; sw_vers -productName; uname -m"); + if (!p.waitForFinished(1000)) return QSysInfo::prettyProductName(); + + QString output = p.readAllStandardOutput().trimmed(); + QStringList lines = output.split("\n"); + if (lines.size() < 3) return QSysInfo::prettyProductName(); + + return QString("%1 %2 (%3)").arg(lines[1]).arg(lines[0]).arg(lines[2]).replace(" ", "_"); +} +#endif + +#ifdef Q_OS_LINUX +QString +get_linux_detailed_version() { + QFile file("/etc/os-release"); + if (!file.open(QIODevice::ReadOnly)) return QSysInfo::prettyProductName(); + + QString prettyName; + while (!file.atEnd()) { + QString line = file.readLine().trimmed(); + if (line.startsWith("PRETTY_NAME=")) { + prettyName = line.section('=', 1).remove('"'); + break; + } + } + file.close(); + + if (prettyName.isEmpty()) return QSysInfo::prettyProductName(); + return prettyName.replace(" ", "_"); +} +#endif + +// User-Agent 格式: +// LiiiSTEM-v2026.2.1 Windows_11_10.0.26100 x86_64 +// LiiiSTEM-v2026.2.1 macOS_15.3_(arm64) arm64 +// LiiiSTEM-v2026.2.1 Debian_GNU/Linux_13_(trixie) x86_64 + +string +qt_http_user_agent(){ + QString appVersion= QString("LiiiSTEM-v") + XMACS_VERSION; +#ifdef Q_OS_WINDOWS + QString osName= get_windows_detailed_version (); +#elif defined(Q_OS_MACOS) + QString osName= get_macos_detailed_version (); +#elif defined(Q_OS_LINUX) + QString osName= get_linux_detailed_version (); +#else + QString osName= QSysInfo::prettyProductName (); +#endif + QString arch= QSysInfo::currentCpuArchitecture (); + + return from_qstring (QString ("%1 %2 %3").arg (appVersion).arg (osName).arg (arch)); +} + +string +qt_http_device_id () { + QByteArray combinedData; + + QList interfaces = QNetworkInterface::allInterfaces(); + for (const QNetworkInterface &interface : interfaces) { + if (!(interface.flags() & QNetworkInterface::IsLoopBack) && + (interface.flags() & QNetworkInterface::IsUp)) { + combinedData.append(interface.hardwareAddress().toUtf8()); + } + } + + QByteArray hashed = QCryptographicHash::hash(combinedData, QCryptographicHash::Sha256); + return from_qstring (QString (hashed.toHex())); +} + void qt_open_url (url u) { debug_io << "open-url\t" << u << LF; diff --git a/src/Plugins/Qt/qt_sys_utils.hpp b/src/Plugins/Qt/qt_sys_utils.hpp index aeeae39e53..5065eff31a 100644 --- a/src/Plugins/Qt/qt_sys_utils.hpp +++ b/src/Plugins/Qt/qt_sys_utils.hpp @@ -17,6 +17,8 @@ string qt_get_current_cpu_arch (); string qt_get_pretty_os_name (); +string qt_http_user_agent (); +string qt_http_device_id (); void qt_open_url (url u); #endif // defined QT_SYS_UTILS_H diff --git a/src/Scheme/Glue/glue_basic.lua b/src/Scheme/Glue/glue_basic.lua index aa5e3c0e8a..f7ec3356d2 100644 --- a/src/Scheme/Glue/glue_basic.lua +++ b/src/Scheme/Glue/glue_basic.lua @@ -1972,6 +1972,16 @@ function main() "url" } }, + { + scm_name = "stem-user-agent", + cpp_name = "http_user_agent", + ret_type = "string" + }, + { + scm_name = "stem-device-id", + cpp_name = "http_device_id", + ret_type = "string" + }, } } end \ No newline at end of file diff --git a/src/System/Misc/tm_sys_utils.cpp b/src/System/Misc/tm_sys_utils.cpp index d1c0c513e2..d9bfe9bb50 100644 --- a/src/System/Misc/tm_sys_utils.cpp +++ b/src/System/Misc/tm_sys_utils.cpp @@ -250,3 +250,25 @@ is_community_stem () { return false; #endif } + +/****************************************************************************** + * HTTP User-Agent and Device ID + ******************************************************************************/ + +string +http_user_agent () { +#ifdef QTTEXMACS + return qt_http_user_agent (); +#else + return "unknown"; +#endif +} + +string +http_device_id () { +#ifdef QTTEXMACS + return qt_http_device_id (); +#else + return "unknown"; +#endif +} diff --git a/src/System/Misc/tm_sys_utils.hpp b/src/System/Misc/tm_sys_utils.hpp index 38d32c7c89..e56358c2fc 100644 --- a/src/System/Misc/tm_sys_utils.hpp +++ b/src/System/Misc/tm_sys_utils.hpp @@ -44,4 +44,7 @@ void system_wait (string message, string argument= "", int level= 0); bool is_community_stem (); +string http_user_agent (); +string http_device_id (); + #endif From 8ff76c3364d1264d7295e8ca46aad546dfdd31b7 Mon Sep 17 00:00:00 2001 From: jiadong Date: Wed, 11 Feb 2026 17:32:11 +0800 Subject: [PATCH 2/3] fix --- devel/200_48.md | 8 +++ src/Plugins/Qt/qt_sys_utils.cpp | 118 +++++++++++++++++++------------- 2 files changed, 77 insertions(+), 49 deletions(-) diff --git a/devel/200_48.md b/devel/200_48.md index 73e35a8c62..2e27aa6cfc 100644 --- a/devel/200_48.md +++ b/devel/200_48.md @@ -1,6 +1,14 @@ # [200_48] User-Agent and Device ID ## 2026/02/11 添加 User-Agent and Device ID 函数 +### 如何测试 +点击`插入交互对话`,使用`scheme`,输入: +``` +(stem-user-agent) +;;期望返回类似:LiiiSTEM-v2026.2.0-rc2 Debian_GNU/Linux_13_(trixie) x86_64 +(stem-device-id) +;;期望返回类似:469709a0db73706427292c7019d41d55f64de1d965367da4206d9d952849120e +``` ### What 提供 HTTP 请求所需的 User-Agent 和设备唯一标识符功能。 diff --git a/src/Plugins/Qt/qt_sys_utils.cpp b/src/Plugins/Qt/qt_sys_utils.cpp index 360f7cbb26..b8ca0886bd 100644 --- a/src/Plugins/Qt/qt_sys_utils.cpp +++ b/src/Plugins/Qt/qt_sys_utils.cpp @@ -16,18 +16,18 @@ #include "tm_configure.hpp" #include "tm_debug.hpp" +#include #include #include #include -#include #include #include #include #include #ifdef Q_OS_WINDOWS -#include #include +#include #endif string @@ -42,66 +42,84 @@ qt_get_pretty_os_name () { #ifdef Q_OS_WINDOWS QString -get_windows_detailed_version() { +get_windows_detailed_version () { RTL_OSVERSIONINFOW osvi; - osvi.dwOSVersionInfoSize = sizeof(osvi); - if (RtlGetVersion(&osvi) != 0) { - return QSysInfo::prettyProductName(); + osvi.dwOSVersionInfoSize= sizeof (osvi); + if (RtlGetVersion (&osvi) != 0) { + return QSysInfo::prettyProductName (); } QString productName; if (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0) { - if (osvi.dwBuildNumber >= 22000) productName = "Windows 11"; - else productName = "Windows 10"; - } else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) { - productName = "Windows 8.1"; - } else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) { - productName = "Windows 8"; - } else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) { - productName = "Windows 7"; - } else { - productName = QString("Windows %1.%2").arg(osvi.dwMajorVersion).arg(osvi.dwMinorVersion); + if (osvi.dwBuildNumber >= 22000) productName= "Windows 11"; + else productName= "Windows 10"; + } + else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) { + productName= "Windows 8.1"; + } + else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) { + productName= "Windows 8"; + } + else if (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) { + productName= "Windows 7"; + } + else { + productName= QString ("Windows %1.%2") + .arg (osvi.dwMajorVersion) + .arg (osvi.dwMinorVersion); } - return QString("%1 %2.%3.%4").arg(productName).arg(osvi.dwMajorVersion).arg(osvi.dwMinorVersion).arg(osvi.dwBuildNumber).replace(" ", "_"); + return QString ("%1 %2.%3.%4") + .arg (productName) + .arg (osvi.dwMajorVersion) + .arg (osvi.dwMinorVersion) + .arg (osvi.dwBuildNumber) + .replace (" ", "_"); } #endif #ifdef Q_OS_MACOS QString -get_macos_detailed_version() { - QProcess p; - QStringList env = QProcess::systemEnvironment(); - p.setEnvironment(env); - p.start("sh", QStringList() << "-c" << "sw_vers -productVersion; sw_vers -productName; uname -m"); - if (!p.waitForFinished(1000)) return QSysInfo::prettyProductName(); - - QString output = p.readAllStandardOutput().trimmed(); - QStringList lines = output.split("\n"); - if (lines.size() < 3) return QSysInfo::prettyProductName(); - - return QString("%1 %2 (%3)").arg(lines[1]).arg(lines[0]).arg(lines[2]).replace(" ", "_"); +get_macos_detailed_version () { + QProcess p; + QStringList env= QProcess::systemEnvironment (); + p.setEnvironment (env); + p.start ("sh", + QStringList () + << "-c" + << "sw_vers -productVersion; sw_vers -productName; uname -m"); + if (!p.waitForFinished (1000)) return QSysInfo::prettyProductName (); + + QString output= p.readAllStandardOutput ().trimmed (); + QStringList lines = output.split ("\n"); + if (lines.size () < 3) return QSysInfo::prettyProductName (); + + return QString ("%1 %2 (%3)") + .arg (lines[1]) + .arg (lines[0]) + .arg (lines[2]) + .replace (" ", "_"); } #endif #ifdef Q_OS_LINUX QString -get_linux_detailed_version() { - QFile file("/etc/os-release"); - if (!file.open(QIODevice::ReadOnly)) return QSysInfo::prettyProductName(); +get_linux_detailed_version () { + QFile file ("/etc/os-release"); + if (!file.open (QIODevice::ReadOnly)) return QSysInfo::prettyProductName (); QString prettyName; - while (!file.atEnd()) { - QString line = file.readLine().trimmed(); - if (line.startsWith("PRETTY_NAME=")) { - prettyName = line.section('=', 1).remove('"'); + while (!file.atEnd ()) { + QString line= file.readLine ().trimmed (); + if (line.startsWith ("PRETTY_NAME=")) { + prettyName= line.section ('=', 1).remove ('"'); break; } } - file.close(); + file.close (); - if (prettyName.isEmpty()) return QSysInfo::prettyProductName(); - return prettyName.replace(" ", "_"); + if (prettyName.isEmpty ()) return QSysInfo::prettyProductName (); + return prettyName.replace (" ", "_"); } #endif @@ -111,8 +129,8 @@ get_linux_detailed_version() { // LiiiSTEM-v2026.2.1 Debian_GNU/Linux_13_(trixie) x86_64 string -qt_http_user_agent(){ - QString appVersion= QString("LiiiSTEM-v") + XMACS_VERSION; +qt_http_user_agent () { + QString appVersion= QString ("LiiiSTEM-v") + XMACS_VERSION; #ifdef Q_OS_WINDOWS QString osName= get_windows_detailed_version (); #elif defined(Q_OS_MACOS) @@ -124,23 +142,25 @@ qt_http_user_agent(){ #endif QString arch= QSysInfo::currentCpuArchitecture (); - return from_qstring (QString ("%1 %2 %3").arg (appVersion).arg (osName).arg (arch)); + return from_qstring ( + QString ("%1 %2 %3").arg (appVersion).arg (osName).arg (arch)); } string qt_http_device_id () { QByteArray combinedData; - QList interfaces = QNetworkInterface::allInterfaces(); - for (const QNetworkInterface &interface : interfaces) { - if (!(interface.flags() & QNetworkInterface::IsLoopBack) && - (interface.flags() & QNetworkInterface::IsUp)) { - combinedData.append(interface.hardwareAddress().toUtf8()); + QList interfaces= QNetworkInterface::allInterfaces (); + for (const QNetworkInterface& interface : interfaces) { + if (!(interface.flags () & QNetworkInterface::IsLoopBack) && + (interface.flags () & QNetworkInterface::IsUp)) { + combinedData.append (interface.hardwareAddress ().toUtf8 ()); } } - QByteArray hashed = QCryptographicHash::hash(combinedData, QCryptographicHash::Sha256); - return from_qstring (QString (hashed.toHex())); + QByteArray hashed= + QCryptographicHash::hash (combinedData, QCryptographicHash::Sha256); + return from_qstring (QString (hashed.toHex ())); } void From 898f0c91f00998b031d1bfedd0f0b9d4cdc097a4 Mon Sep 17 00:00:00 2001 From: jiadong Date: Wed, 11 Feb 2026 17:38:05 +0800 Subject: [PATCH 3/3] http->stem --- src/Plugins/Qt/qt_sys_utils.cpp | 4 ++-- src/Plugins/Qt/qt_sys_utils.hpp | 4 ++-- src/Scheme/Glue/glue_basic.lua | 4 ++-- src/System/Misc/tm_sys_utils.cpp | 10 +++++----- src/System/Misc/tm_sys_utils.hpp | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Plugins/Qt/qt_sys_utils.cpp b/src/Plugins/Qt/qt_sys_utils.cpp index b8ca0886bd..e3c9f8c0ab 100644 --- a/src/Plugins/Qt/qt_sys_utils.cpp +++ b/src/Plugins/Qt/qt_sys_utils.cpp @@ -129,7 +129,7 @@ get_linux_detailed_version () { // LiiiSTEM-v2026.2.1 Debian_GNU/Linux_13_(trixie) x86_64 string -qt_http_user_agent () { +qt_stem_user_agent () { QString appVersion= QString ("LiiiSTEM-v") + XMACS_VERSION; #ifdef Q_OS_WINDOWS QString osName= get_windows_detailed_version (); @@ -147,7 +147,7 @@ qt_http_user_agent () { } string -qt_http_device_id () { +qt_stem_device_id () { QByteArray combinedData; QList interfaces= QNetworkInterface::allInterfaces (); diff --git a/src/Plugins/Qt/qt_sys_utils.hpp b/src/Plugins/Qt/qt_sys_utils.hpp index 5065eff31a..1bf43519ff 100644 --- a/src/Plugins/Qt/qt_sys_utils.hpp +++ b/src/Plugins/Qt/qt_sys_utils.hpp @@ -17,8 +17,8 @@ string qt_get_current_cpu_arch (); string qt_get_pretty_os_name (); -string qt_http_user_agent (); -string qt_http_device_id (); +string qt_stem_user_agent (); +string qt_stem_device_id (); void qt_open_url (url u); #endif // defined QT_SYS_UTILS_H diff --git a/src/Scheme/Glue/glue_basic.lua b/src/Scheme/Glue/glue_basic.lua index f7ec3356d2..35e17d92cd 100644 --- a/src/Scheme/Glue/glue_basic.lua +++ b/src/Scheme/Glue/glue_basic.lua @@ -1974,12 +1974,12 @@ function main() }, { scm_name = "stem-user-agent", - cpp_name = "http_user_agent", + cpp_name = "stem_user_agent", ret_type = "string" }, { scm_name = "stem-device-id", - cpp_name = "http_device_id", + cpp_name = "stem_device_id", ret_type = "string" }, } diff --git a/src/System/Misc/tm_sys_utils.cpp b/src/System/Misc/tm_sys_utils.cpp index d9bfe9bb50..0e1b9d0ff9 100644 --- a/src/System/Misc/tm_sys_utils.cpp +++ b/src/System/Misc/tm_sys_utils.cpp @@ -252,22 +252,22 @@ is_community_stem () { } /****************************************************************************** - * HTTP User-Agent and Device ID + * STEM User-Agent and Device ID ******************************************************************************/ string -http_user_agent () { +stem_user_agent () { #ifdef QTTEXMACS - return qt_http_user_agent (); + return qt_stem_user_agent (); #else return "unknown"; #endif } string -http_device_id () { +stem_device_id () { #ifdef QTTEXMACS - return qt_http_device_id (); + return qt_stem_device_id (); #else return "unknown"; #endif diff --git a/src/System/Misc/tm_sys_utils.hpp b/src/System/Misc/tm_sys_utils.hpp index e56358c2fc..fbef2ad535 100644 --- a/src/System/Misc/tm_sys_utils.hpp +++ b/src/System/Misc/tm_sys_utils.hpp @@ -44,7 +44,7 @@ void system_wait (string message, string argument= "", int level= 0); bool is_community_stem (); -string http_user_agent (); -string http_device_id (); +string stem_user_agent (); +string stem_device_id (); #endif