Improve master authorization logs and web remote desktop cursor
This commit is contained in:
@@ -272,6 +272,7 @@ DWORD WINAPI ExecuteDLLProc(LPVOID param)
|
|||||||
FrpcParam* f = (FrpcParam*)user;
|
FrpcParam* f = (FrpcParam*)user;
|
||||||
Mprintf("MemoryGetProcAddress '%s' %s\n", info.Name, proc ? "success" : "failed");
|
Mprintf("MemoryGetProcAddress '%s' %s\n", info.Name, proc ? "success" : "failed");
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
uint64_t start = time(0);
|
||||||
if (proc) {
|
if (proc) {
|
||||||
r=proc(f->privilegeKey, f->timestamp, f->serverAddr, f->serverPort, f->localPort, f->remotePort,
|
r=proc(f->privilegeKey, f->timestamp, f->serverAddr, f->serverPort, f->localPort, f->remotePort,
|
||||||
&CKernelManager::g_IsAppExit);
|
&CKernelManager::g_IsAppExit);
|
||||||
@@ -279,7 +280,7 @@ DWORD WINAPI ExecuteDLLProc(LPVOID param)
|
|||||||
else {
|
else {
|
||||||
This->m_cfg->SetStr("settings", info.Name + std::string(".md5"), "");
|
This->m_cfg->SetStr("settings", info.Name + std::string(".md5"), "");
|
||||||
}
|
}
|
||||||
if (r) {
|
if (r || (time(0)-start < 15)) {
|
||||||
char buf[100];
|
char buf[100];
|
||||||
sprintf_s(buf, "Run %s [proxy %d] failed: %d", info.Name, f->localPort, r);
|
sprintf_s(buf, "Run %s [proxy %d] failed: %d", info.Name, f->localPort, r);
|
||||||
Mprintf("%s\n", buf);
|
Mprintf("%s\n", buf);
|
||||||
@@ -295,6 +296,7 @@ DWORD WINAPI ExecuteDLLProc(LPVOID param)
|
|||||||
FrpcParam* f = (FrpcParam*)user;
|
FrpcParam* f = (FrpcParam*)user;
|
||||||
Mprintf("MemoryGetProcAddress '%s' %s\n", info.Name, proc ? "success" : "failed");
|
Mprintf("MemoryGetProcAddress '%s' %s\n", info.Name, proc ? "success" : "failed");
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
uint64_t start = time(0);
|
||||||
if (proc) {
|
if (proc) {
|
||||||
r = proc(f->privilegeKey, f->serverAddr, f->serverPort, f->localPort, f->remotePort,
|
r = proc(f->privilegeKey, f->serverAddr, f->serverPort, f->localPort, f->remotePort,
|
||||||
&CKernelManager::g_IsAppExit);
|
&CKernelManager::g_IsAppExit);
|
||||||
@@ -302,7 +304,7 @@ DWORD WINAPI ExecuteDLLProc(LPVOID param)
|
|||||||
else {
|
else {
|
||||||
This->m_cfg->SetStr("settings", info.Name + std::string(".md5"), "");
|
This->m_cfg->SetStr("settings", info.Name + std::string(".md5"), "");
|
||||||
}
|
}
|
||||||
if (r) {
|
if (r || (time(0)-start < 15)) {
|
||||||
char buf[100];
|
char buf[100];
|
||||||
sprintf_s(buf, "Run %s [proxy %d] failed: %d", info.Name, f->localPort, r);
|
sprintf_s(buf, "Run %s [proxy %d] failed: %d", info.Name, f->localPort, r);
|
||||||
Mprintf("%s\n", buf);
|
Mprintf("%s\n", buf);
|
||||||
|
|||||||
@@ -330,6 +330,7 @@ enum {
|
|||||||
CMD_SET_GROUP = 242, // 修改分组
|
CMD_SET_GROUP = 242, // 修改分组
|
||||||
CMD_EXECUTE_DLL_NEW = 243, // 执行代码
|
CMD_EXECUTE_DLL_NEW = 243, // 执行代码
|
||||||
CMD_PEER_TO_PEER = 244, // P2P通信
|
CMD_PEER_TO_PEER = 244, // P2P通信
|
||||||
|
TOKEN_CLIENTID = 245,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MachineCommand {
|
enum MachineCommand {
|
||||||
|
|||||||
119
macos/install.sh
119
macos/install.sh
@@ -4,100 +4,101 @@
|
|||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
GHOST_SRC="${1:-$SCRIPT_DIR/build/bin/ghost}"
|
GHOST_SRC="${1:-$SCRIPT_DIR/build/bin/ghost}"
|
||||||
GHOST_DST="/usr/local/bin/ghost"
|
APP_DIR="/Applications/GhostClient.app"
|
||||||
PLIST_DST="/Library/LaunchDaemons/com.ghost.client.plist"
|
APP_BIN="$APP_DIR/Contents/MacOS/ghost"
|
||||||
|
|
||||||
echo "=== Ghost Client 安装程序 ==="
|
echo "=== GhostClient 安装程序 ==="
|
||||||
echo "源文件: $GHOST_SRC"
|
echo ""
|
||||||
|
|
||||||
# 检查源文件
|
# 检查源文件
|
||||||
if [ ! -f "$GHOST_SRC" ]; then
|
if [ ! -f "$GHOST_SRC" ]; then
|
||||||
echo ""
|
|
||||||
echo "错误: 找不到 $GHOST_SRC"
|
echo "错误: 找不到 $GHOST_SRC"
|
||||||
echo ""
|
echo ""
|
||||||
echo "请先编译: ./build.sh"
|
echo "请先编译: ./build.sh"
|
||||||
echo ""
|
|
||||||
echo "或指定路径: $0 <ghost可执行文件路径>"
|
echo "或指定路径: $0 <ghost可执行文件路径>"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "源文件: $GHOST_SRC"
|
||||||
|
echo ""
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# 1. 停止旧服务(只停止安装目录的,不影响调试目录)
|
# 1. 停止旧进程
|
||||||
echo "[1/6] 停止旧服务..."
|
echo "[1/6] 停止旧进程..."
|
||||||
sudo launchctl unload "$PLIST_DST" 2>/dev/null || true
|
pkill -9 -f "$APP_BIN" 2>/dev/null || true
|
||||||
sudo pkill -9 -f "$GHOST_DST" 2>/dev/null || true
|
|
||||||
|
|
||||||
# 2. 复制程序
|
# 2. 重置系统权限(关键步骤!避免权限缓存导致空白桌面)
|
||||||
echo "[2/6] 安装程序到 $GHOST_DST..."
|
echo "[2/6] 重置系统权限..."
|
||||||
sudo cp "$GHOST_SRC" "$GHOST_DST"
|
echo " (这会清除屏幕录制和辅助功能的旧授权,需要重新授权)"
|
||||||
sudo chmod +x "$GHOST_DST"
|
tccutil reset ScreenCapture 2>/dev/null || true
|
||||||
|
tccutil reset Accessibility 2>/dev/null || true
|
||||||
|
|
||||||
# 3. 清除隔离属性
|
# 3. 创建应用程序包
|
||||||
echo "[3/6] 清除隔离属性..."
|
echo "[3/6] 创建应用程序..."
|
||||||
sudo xattr -cr "$GHOST_DST"
|
sudo rm -rf "$APP_DIR"
|
||||||
|
sudo mkdir -p "$APP_DIR/Contents/MacOS"
|
||||||
|
sudo mkdir -p "$APP_DIR/Contents/Resources"
|
||||||
|
|
||||||
# 4. 签名
|
# 复制 ghost 到 app bundle 内部
|
||||||
echo "[4/6] 签名程序..."
|
sudo cp "$GHOST_SRC" "$APP_BIN"
|
||||||
sudo codesign --force --deep --sign - "$GHOST_DST"
|
sudo chmod +x "$APP_BIN"
|
||||||
|
|
||||||
# 5. 创建 launchd plist
|
# 创建 Info.plist
|
||||||
echo "[5/6] 创建 launchd 服务..."
|
sudo tee "$APP_DIR/Contents/Info.plist" > /dev/null << 'EOF'
|
||||||
sudo tee "$PLIST_DST" > /dev/null << 'EOF'
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>Label</key>
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>ghost</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.ghost.client</string>
|
<string>com.ghost.client</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
<key>ProgramArguments</key>
|
<string>GhostClient</string>
|
||||||
<array>
|
<key>CFBundleVersion</key>
|
||||||
<string>/usr/local/bin/ghost</string>
|
<string>1.0</string>
|
||||||
</array>
|
<key>LSUIElement</key>
|
||||||
|
|
||||||
<key>RunAtLoad</key>
|
|
||||||
<true/>
|
<true/>
|
||||||
|
|
||||||
<key>KeepAlive</key>
|
|
||||||
<true/>
|
|
||||||
|
|
||||||
<key>StandardOutPath</key>
|
|
||||||
<string>/var/log/ghost.log</string>
|
|
||||||
|
|
||||||
<key>StandardErrorPath</key>
|
|
||||||
<string>/var/log/ghost.log</string>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
sudo chown root:wheel "$PLIST_DST"
|
# 4. 清除隔离属性
|
||||||
sudo chmod 644 "$PLIST_DST"
|
echo "[4/6] 清除隔离属性..."
|
||||||
|
sudo xattr -cr "$APP_DIR"
|
||||||
|
|
||||||
# 6. 完成
|
# 5. 签名应用
|
||||||
echo "[6/6] 安装完成!"
|
echo "[5/6] 签名应用..."
|
||||||
|
sudo codesign --force --deep --sign - "$APP_DIR"
|
||||||
|
|
||||||
|
# 6. 添加到登录项(开机自启)
|
||||||
|
echo "[6/7] 添加到登录项..."
|
||||||
|
osascript -e 'tell application "System Events" to delete login item "GhostClient"' 2>/dev/null || true
|
||||||
|
osascript -e 'tell application "System Events" to make login item at end with properties {path:"/Applications/GhostClient.app", hidden:true}' 2>/dev/null && echo " 已添加开机自启" || echo " 添加失败,请手动添加"
|
||||||
|
|
||||||
|
# 7. 完成
|
||||||
|
echo "[7/7] 安装完成!"
|
||||||
echo ""
|
echo ""
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
echo "重要: 首次运行需要授权系统权限"
|
echo " 下一步: 授权系统权限"
|
||||||
echo "========================================"
|
echo "========================================"
|
||||||
echo ""
|
echo ""
|
||||||
echo "请执行以下步骤:"
|
echo "1. 启动应用 (会自动弹出权限请求):"
|
||||||
echo ""
|
echo ""
|
||||||
echo "1. 手动运行以触发权限请求:"
|
echo " open /Applications/GhostClient.app"
|
||||||
echo " $GHOST_DST"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "2. 授权后按 Ctrl+C 停止程序(权限需重启生效)"
|
echo "2. 授权以下权限 (在系统设置中勾选 GhostClient):"
|
||||||
|
echo " - 屏幕录制: 允许捕获屏幕画面"
|
||||||
|
echo " - 辅助功能: 允许控制鼠标键盘"
|
||||||
echo ""
|
echo ""
|
||||||
echo "3. 启动服务:"
|
echo "3. 授权后重启应用:"
|
||||||
echo " sudo launchctl load $PLIST_DST"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "如未弹出授权对话框,手动添加:"
|
echo " pkill -f GhostClient && open /Applications/GhostClient.app"
|
||||||
echo " 系统设置 > 隐私与安全性 > 屏幕录制 > 添加 ghost"
|
|
||||||
echo " 系统设置 > 隐私与安全性 > 辅助功能 > 添加 ghost"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "常用命令:"
|
echo "4. 查看日志确认运行状态:"
|
||||||
echo " 启动: sudo launchctl start com.ghost.client"
|
echo ""
|
||||||
echo " 停止: sudo launchctl stop com.ghost.client"
|
echo " tail -f /tmp/ghost.log"
|
||||||
echo " 卸载: sudo launchctl unload $PLIST_DST"
|
echo ""
|
||||||
echo " 日志: tail -f /var/log/ghost.log"
|
echo "========================================"
|
||||||
echo ""
|
echo ""
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# macOS Ghost Client 卸载脚本
|
# macOS Ghost Client 卸载脚本
|
||||||
|
|
||||||
echo "=== Ghost Client 卸载程序 ==="
|
APP_DIR="/Applications/GhostClient.app"
|
||||||
|
|
||||||
# 1. 停止并卸载 launchd 服务
|
echo "=== GhostClient 卸载程序 ==="
|
||||||
echo "[1/4] 停止服务..."
|
|
||||||
sudo launchctl unload /Library/LaunchDaemons/com.ghost.client.plist 2>/dev/null
|
|
||||||
launchctl unload ~/Library/LaunchAgents/com.ghost.client.plist 2>/dev/null
|
|
||||||
|
|
||||||
# 2. 杀死残留进程
|
|
||||||
echo "[2/4] 终止进程..."
|
|
||||||
sudo pkill -9 -f "/usr/local/bin/ghost" 2>/dev/null
|
|
||||||
|
|
||||||
# 3. 删除文件
|
|
||||||
echo "[3/4] 删除文件..."
|
|
||||||
sudo rm -f /Library/LaunchDaemons/com.ghost.client.plist
|
|
||||||
rm -f ~/Library/LaunchAgents/com.ghost.client.plist
|
|
||||||
sudo rm -f /usr/local/bin/ghost
|
|
||||||
rm -rf ~/.config/ghost
|
|
||||||
sudo rm -f /var/log/ghost.log
|
|
||||||
|
|
||||||
# 4. 完成
|
|
||||||
echo "[4/4] 卸载完成!"
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "注意: 系统权限(屏幕录制/辅助功能)未重置。"
|
|
||||||
|
# 1. 停止进程
|
||||||
|
echo "[1/3] 停止进程..."
|
||||||
|
pkill -9 -f "$APP_DIR" 2>/dev/null || true
|
||||||
|
|
||||||
|
# 2. 删除文件
|
||||||
|
echo "[2/3] 删除文件..."
|
||||||
|
sudo rm -rf "$APP_DIR"
|
||||||
|
rm -rf ~/.config/ghost 2>/dev/null || true
|
||||||
|
rm -f /tmp/ghost.log 2>/dev/null || true
|
||||||
|
|
||||||
|
# 3. 移除登录项
|
||||||
|
echo "[3/4] 移除登录项..."
|
||||||
|
osascript -e 'tell application "System Events" to delete login item "GhostClient"' 2>/dev/null || true
|
||||||
|
|
||||||
|
# 4. 重置系统权限
|
||||||
|
echo "[4/4] 重置系统权限..."
|
||||||
|
tccutil reset ScreenCapture 2>/dev/null || true
|
||||||
|
tccutil reset Accessibility 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================"
|
||||||
|
echo " 卸载完成"
|
||||||
|
echo "========================================"
|
||||||
echo ""
|
echo ""
|
||||||
echo "如需重置系统权限(会影响所有应用),请手动执行:"
|
|
||||||
echo " tccutil reset ScreenCapture"
|
|
||||||
echo " tccutil reset Accessibility"
|
|
||||||
echo " tccutil reset SystemPolicyAllFiles"
|
|
||||||
|
|||||||
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 {
|
struct AuthLogState {
|
||||||
bool lastStatus;
|
int lastStatus;
|
||||||
time_t lastLogTime;
|
time_t lastLogTime;
|
||||||
};
|
};
|
||||||
static std::map<std::string, AuthLogState> s_cache;
|
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);
|
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 (outExpired) *outExpired = false;
|
||||||
|
|
||||||
if (sn.empty() || passcode.empty() || hmac == 0) {
|
if (sn.empty() || passcode.empty() || hmac == 0) {
|
||||||
return FALSE;
|
return 1;
|
||||||
}
|
}
|
||||||
auto v = splitString(passcode, '-');
|
auto v = splitString(passcode, '-');
|
||||||
if (v.size() == 6 || v.size() == 7) {
|
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 hash256 = joinString(subvector, '-');
|
||||||
std::string fixedKey = getFixedLengthID(finalKey);
|
std::string fixedKey = getFixedLengthID(finalKey);
|
||||||
if (hash256 != fixedKey)
|
if (hash256 != fixedKey)
|
||||||
return FALSE;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* superAdmin = getenv(BRAND_ENV_VAR);
|
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");
|
Mprintf("请设置环境变量 " BRAND_ENV_VAR " 来给下级授权!\n");
|
||||||
}
|
}
|
||||||
BOOL b = VerifyMessage(pwd, (BYTE*)passcode.c_str(), passcode.length(), hmac);
|
BOOL b = VerifyMessage(pwd, (BYTE*)passcode.c_str(), passcode.length(), hmac);
|
||||||
if (!b) return FALSE;
|
if (!b) return 3;
|
||||||
auto list = StringToVector(passcode, '-', 2);
|
auto list = StringToVector(passcode, '-', 2);
|
||||||
BOOL valid = IsDateInRange(list[0], list[1]);
|
BOOL valid = IsDateInRange(list[0], list[1]);
|
||||||
std::string hmacStr = std::to_string(hmac);
|
std::string hmacStr = std::to_string(hmac);
|
||||||
|
|
||||||
// 授权过期,更新或创建记录并标记为过期
|
// 授权过期,更新或创建记录并标记为过期
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
Mprintf("授权已过期: %s\n", sn.c_str());
|
|
||||||
if (outExpired) *outExpired = true; // 签名有效但已过期
|
if (outExpired) *outExpired = true; // 签名有效但已过期
|
||||||
if (ctx != nullptr) {
|
if (ctx != nullptr) {
|
||||||
std::string ip = ctx->GetClientData(ONLINELIST_IP);
|
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);
|
UpdateLicenseActivity(sn, passcode, hmacStr);
|
||||||
}
|
}
|
||||||
SetLicenseStatus(sn, LICENSE_STATUS_EXPIRED);
|
SetLicenseStatus(sn, LICENSE_STATUS_EXPIRED);
|
||||||
return FALSE;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查授权是否已被撤销
|
// 检查授权是否已被撤销
|
||||||
if (IsLicenseRevoked(sn)) {
|
if (IsLicenseRevoked(sn)) {
|
||||||
Mprintf("授权已被撤销: %s\n", sn.c_str());
|
return 5;
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 授权成功时更新 license 活跃信息
|
// 授权成功时更新 license 活跃信息
|
||||||
@@ -4587,21 +4585,20 @@ BOOL CMy2015RemoteDlg::AuthorizeClient(context* ctx, const std::string& sn, cons
|
|||||||
UpdateLicenseActivity(sn, passcode, hmacStr);
|
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 (outExpired) *outExpired = false;
|
||||||
|
|
||||||
if (sn.empty() || passcode.empty() || hmacV2.empty()) {
|
if (sn.empty() || passcode.empty() || hmacV2.empty()) {
|
||||||
return FALSE;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查 V2 前缀
|
// 检查 V2 前缀
|
||||||
if (hmacV2.substr(0, 3) != "v2:") {
|
if (hmacV2.substr(0, 3) != "v2:") {
|
||||||
Mprintf("V2 HMAC 格式错误: %s\n", hmacV2.c_str());
|
return 2;
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查公钥是否已配置(全零表示未配置)
|
// 检查公钥是否已配置(全零表示未配置)
|
||||||
@@ -4613,15 +4610,13 @@ BOOL CMy2015RemoteDlg::AuthorizeClientV2(context* ctx, const std::string& sn, co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!keyConfigured) {
|
if (!keyConfigured) {
|
||||||
Mprintf("V2 公钥未配置,无法验证 V2 授权\n");
|
return 3;
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 V2 验证
|
// 使用 V2 验证
|
||||||
BOOL b = verifyPasswordV2(sn, passcode, hmacV2, g_LicensePublicKey);
|
BOOL b = verifyPasswordV2(sn, passcode, hmacV2, g_LicensePublicKey);
|
||||||
if (!b) {
|
if (!b) {
|
||||||
Mprintf("V2 签名验证失败: %s\n", sn.c_str());
|
return 4;
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto list = StringToVector(passcode, '-', 2);
|
auto list = StringToVector(passcode, '-', 2);
|
||||||
@@ -4629,7 +4624,6 @@ BOOL CMy2015RemoteDlg::AuthorizeClientV2(context* ctx, const std::string& sn, co
|
|||||||
|
|
||||||
// 授权过期
|
// 授权过期
|
||||||
if (!valid) {
|
if (!valid) {
|
||||||
Mprintf("V2 授权已过期: %s\n", sn.c_str());
|
|
||||||
if (outExpired) *outExpired = true; // 签名有效但已过期
|
if (outExpired) *outExpired = true; // 签名有效但已过期
|
||||||
if (ctx != nullptr) {
|
if (ctx != nullptr) {
|
||||||
std::string ip = ctx->GetClientData(ONLINELIST_IP);
|
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);
|
UpdateLicenseActivity(sn, passcode, hmacV2);
|
||||||
}
|
}
|
||||||
SetLicenseStatus(sn, LICENSE_STATUS_EXPIRED);
|
SetLicenseStatus(sn, LICENSE_STATUS_EXPIRED);
|
||||||
return FALSE;
|
return 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查授权是否已被撤销
|
// 检查授权是否已被撤销
|
||||||
if (IsLicenseRevoked(sn)) {
|
if (IsLicenseRevoked(sn)) {
|
||||||
Mprintf("V2 授权已被撤销: %s\n", sn.c_str());
|
return 6;
|
||||||
return FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 授权成功时更新 license 活跃信息
|
// 授权成功时更新 license 活跃信息
|
||||||
@@ -4659,7 +4652,7 @@ BOOL CMy2015RemoteDlg::AuthorizeClientV2(context* ctx, const std::string& sn, co
|
|||||||
UpdateLicenseActivity(sn, passcode, hmacV2);
|
UpdateLicenseActivity(sn, passcode, hmacV2);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL IsTrail(const std::string& passcode)
|
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& sn, const std::string& passcode, uint64_t hmac,
|
||||||
const std::string& hmacV2, const std::string& ip, const char* source)
|
const std::string& hmacV2, const std::string& ip, const char* source)
|
||||||
{
|
{
|
||||||
bool authorized = false;
|
BOOL authorized = -1;
|
||||||
bool isV2 = false;
|
bool isV2 = false;
|
||||||
bool isTrail = false;
|
bool isTrail = false;
|
||||||
bool expired = false;
|
bool expired = false;
|
||||||
@@ -5794,19 +5787,19 @@ std::tuple<bool, bool, bool, bool> CMy2015RemoteDlg::VerifyClientAuth(context* h
|
|||||||
// V2 授权验证
|
// V2 授权验证
|
||||||
isV2 = true;
|
isV2 = true;
|
||||||
authorized = AuthorizeClientV2(host, sn, passcode, hmacV2, &expired);
|
authorized = AuthorizeClientV2(host, sn, passcode, hmacV2, &expired);
|
||||||
if (authorized) {
|
if (authorized == 0) {
|
||||||
if (host) {
|
if (host) {
|
||||||
m_ClientMap->SetClientMapInteger(host->GetClientID(), MAP_AUTH, TRUE);
|
m_ClientMap->SetClientMapInteger(host->GetClientID(), MAP_AUTH, TRUE);
|
||||||
}
|
}
|
||||||
isTrail = IsTrail(passcode.c_str());
|
isTrail = IsTrail(passcode.c_str());
|
||||||
}
|
}
|
||||||
if (ShouldLogAuth(sn, authorized)) {
|
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());
|
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 + "]";
|
std::string tip = passcode + std::string(_L(" V2 授权成功: ")) + sn + "[" + ip + "]";
|
||||||
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
||||||
} else {
|
} 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 + "]";
|
std::string tip = passcode + std::string(_L(" V2 授权失败: ")) + sn + "[" + ip + "]";
|
||||||
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
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 授权验证
|
// V1 授权验证
|
||||||
isV2 = false;
|
isV2 = false;
|
||||||
authorized = AuthorizeClient(host, sn, passcode, hmac, &expired);
|
authorized = AuthorizeClient(host, sn, passcode, hmac, &expired);
|
||||||
if (authorized) {
|
if (authorized == 0) {
|
||||||
if (host) {
|
if (host) {
|
||||||
m_ClientMap->SetClientMapInteger(host->GetClientID(), MAP_AUTH, TRUE);
|
m_ClientMap->SetClientMapInteger(host->GetClientID(), MAP_AUTH, TRUE);
|
||||||
}
|
}
|
||||||
isTrail = IsTrail(passcode.c_str());
|
isTrail = IsTrail(passcode.c_str());
|
||||||
}
|
}
|
||||||
if (ShouldLogAuth(sn, authorized)) {
|
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());
|
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 + "]";
|
std::string tip = passcode + std::string(_L(" V1 授权成功: ")) + sn + "[" + ip + "]";
|
||||||
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
||||||
} else {
|
} 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 + "]";
|
std::string tip = passcode + std::string(_L(" V1 授权失败: ")) + sn + "[" + ip + "]";
|
||||||
PostMessageA(WM_SHOWMESSAGE, (WPARAM)new CharMsg(tip.c_str()), NULL);
|
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;
|
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)
|
LRESULT CMy2015RemoteDlg::InjectShellcode(WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
std::string* ip = (std::string*)wParam;
|
std::string* ip = (std::string*)wParam;
|
||||||
|
|||||||
@@ -215,8 +215,8 @@ public:
|
|||||||
MasterSettings m_settings;
|
MasterSettings m_settings;
|
||||||
static BOOL CALLBACK NotifyProc(CONTEXT_OBJECT* ContextObject);
|
static BOOL CALLBACK NotifyProc(CONTEXT_OBJECT* ContextObject);
|
||||||
static BOOL CALLBACK OfflineProc(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);
|
int 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 AuthorizeClientV2(context* ctx, const std::string& sn, const std::string& passcode, const std::string& hmacV2, bool* outExpired = nullptr);
|
||||||
VOID MessageHandle(CONTEXT_OBJECT* ContextObject);
|
VOID MessageHandle(CONTEXT_OBJECT* ContextObject);
|
||||||
VOID SendSelectedCommand(PBYTE szBuffer, ULONG ulLength, contextModifier cb = NULL, void* user=NULL);
|
VOID SendSelectedCommand(PBYTE szBuffer, ULONG ulLength, contextModifier cb = NULL, void* user=NULL);
|
||||||
VOID SendAllCommand(PBYTE szBuffer, ULONG ulLength);
|
VOID SendAllCommand(PBYTE szBuffer, ULONG ulLength);
|
||||||
@@ -255,6 +255,7 @@ public:
|
|||||||
CGridDialog * m_gridDlg = NULL;
|
CGridDialog * m_gridDlg = NULL;
|
||||||
std::vector<DllInfo*> m_DllList;
|
std::vector<DllInfo*> m_DllList;
|
||||||
context* FindHostByIP(const std::string& ip);
|
context* FindHostByIP(const std::string& ip);
|
||||||
|
uint64_t FindClientIDByIP(const std::string& ip); // 线程安全:在锁内获取ID
|
||||||
void InjectTinyRunDll(const std::string& ip, int pid);
|
void InjectTinyRunDll(const std::string& ip, int pid);
|
||||||
NOTIFYICONDATA m_Nid;
|
NOTIFYICONDATA m_Nid;
|
||||||
HANDLE m_hExit;
|
HANDLE m_hExit;
|
||||||
|
|||||||
@@ -25,6 +25,26 @@ static UINT indicators[] = {
|
|||||||
#define MAX_SEND_BUFFER 65535
|
#define MAX_SEND_BUFFER 65535
|
||||||
#define MAX_RECV_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 {
|
typedef struct {
|
||||||
LVITEM* plvi;
|
LVITEM* plvi;
|
||||||
CString sCol2;
|
CString sCol2;
|
||||||
@@ -137,10 +157,12 @@ BEGIN_MESSAGE_MAP(CFileManagerDlg, CDialog)
|
|||||||
ON_COMMAND(IDT_LOCAL_DOWNLOADS, OnLocalDownloads)
|
ON_COMMAND(IDT_LOCAL_DOWNLOADS, OnLocalDownloads)
|
||||||
ON_COMMAND(IDT_LOCAL_HOME, OnLocalHome)
|
ON_COMMAND(IDT_LOCAL_HOME, OnLocalHome)
|
||||||
ON_COMMAND(IDT_LOCAL_SEARCH, OnLocalSearch)
|
ON_COMMAND(IDT_LOCAL_SEARCH, OnLocalSearch)
|
||||||
|
ON_COMMAND(IDT_LOCAL_HISTORY, OnLocalHistory)
|
||||||
ON_COMMAND(IDT_REMOTE_DESKTOP, OnRemoteDesktop)
|
ON_COMMAND(IDT_REMOTE_DESKTOP, OnRemoteDesktop)
|
||||||
ON_COMMAND(IDT_REMOTE_DOWNLOADS, OnRemoteDownloads)
|
ON_COMMAND(IDT_REMOTE_DOWNLOADS, OnRemoteDownloads)
|
||||||
ON_COMMAND(IDT_REMOTE_HOME, OnRemoteHome)
|
ON_COMMAND(IDT_REMOTE_HOME, OnRemoteHome)
|
||||||
ON_COMMAND(IDT_REMOTE_SEARCH, OnRemoteSearch)
|
ON_COMMAND(IDT_REMOTE_SEARCH, OnRemoteSearch)
|
||||||
|
ON_COMMAND(IDT_REMOTE_HISTORY, OnRemoteHistory)
|
||||||
ON_COMMAND(IDM_TRANSFER, OnTransfer)
|
ON_COMMAND(IDM_TRANSFER, OnTransfer)
|
||||||
ON_COMMAND(IDM_RENAME, OnRename)
|
ON_COMMAND(IDM_RENAME, OnRename)
|
||||||
ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_LOCAL, OnEndlabeleditListLocal)
|
ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST_LOCAL, OnEndlabeleditListLocal)
|
||||||
@@ -494,6 +516,12 @@ void CFileManagerDlg::FixedLocalFileList(CString directory)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ShowMessage(_TRF("本地:装载目录 %s 完成"), m_Local_Path);
|
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)
|
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);
|
ShowMessage(_TRF("搜索 \"%s\" 在 %s 完成,共 %d 个结果 (耗时 %d秒)"), m_strSearchName, m_strSearchPath, m_nSearchResultCount, dwElapsed);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TOKEN_CLIENTID:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
SendException();
|
SendException();
|
||||||
break;
|
break;
|
||||||
@@ -1024,6 +1054,13 @@ void CFileManagerDlg::GetRemoteFileList(CString directory)
|
|||||||
m_Remote_Directory_ComboBox.InsertStringL(0, m_Remote_Path);
|
m_Remote_Directory_ComboBox.InsertStringL(0, m_Remote_Path);
|
||||||
m_Remote_Directory_ComboBox.SetCurSel(0);
|
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_list_remote.EnableWindow(FALSE);
|
||||||
m_ProgressCtrl->SetPos(0);
|
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()
|
void CFileManagerDlg::OnLocalView()
|
||||||
{
|
{
|
||||||
// TODO: Add your command handler code here
|
// TODO: Add your command handler code here
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "IOCPServer.h"
|
#include "IOCPServer.h"
|
||||||
#include "SortListCtrl.h"
|
#include "SortListCtrl.h"
|
||||||
|
#include "../../common/locker.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -246,10 +247,12 @@ protected:
|
|||||||
afx_msg void OnLocalDownloads();
|
afx_msg void OnLocalDownloads();
|
||||||
afx_msg void OnLocalHome();
|
afx_msg void OnLocalHome();
|
||||||
afx_msg void OnLocalSearch();
|
afx_msg void OnLocalSearch();
|
||||||
|
afx_msg void OnLocalHistory();
|
||||||
afx_msg void OnRemoteDesktop();
|
afx_msg void OnRemoteDesktop();
|
||||||
afx_msg void OnRemoteDownloads();
|
afx_msg void OnRemoteDownloads();
|
||||||
afx_msg void OnRemoteHome();
|
afx_msg void OnRemoteHome();
|
||||||
afx_msg void OnRemoteSearch();
|
afx_msg void OnRemoteSearch();
|
||||||
|
afx_msg void OnRemoteHistory();
|
||||||
afx_msg void OnTransferV2ToRemote(); // V2: 本地文件传输到远程
|
afx_msg void OnTransferV2ToRemote(); // V2: 本地文件传输到远程
|
||||||
afx_msg void OnTransferV2ToLocal(); // V2: 远程文件传输到本地
|
afx_msg void OnTransferV2ToLocal(); // V2: 远程文件传输到本地
|
||||||
//}}AFX_MSG
|
//}}AFX_MSG
|
||||||
@@ -274,6 +277,14 @@ private:
|
|||||||
void EnableControl(BOOL bEnable = TRUE);
|
void EnableControl(BOOL bEnable = TRUE);
|
||||||
void CollectFilesRecursive(const std::string& dirPath, std::vector<std::string>& files);
|
void CollectFilesRecursive(const std::string& dirPath, std::vector<std::string>& files);
|
||||||
float m_fScalingFactor;
|
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:
|
public:
|
||||||
afx_msg void OnFilemangerCompress();
|
afx_msg void OnFilemangerCompress();
|
||||||
afx_msg void OnFilemangerUncompress();
|
afx_msg void OnFilemangerUncompress();
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ public:
|
|||||||
{
|
{
|
||||||
return m_bIsClosed;
|
return m_bIsClosed;
|
||||||
}
|
}
|
||||||
uint64_t GetClientID() const {
|
virtual uint64_t GetClientID() const {
|
||||||
return m_ClientID;
|
return m_ClientID;
|
||||||
}
|
}
|
||||||
BOOL SayByeBye()
|
BOOL SayByeBye()
|
||||||
|
|||||||
@@ -1846,13 +1846,15 @@ inline std::string GetWebPageHTML() {
|
|||||||
|
|
||||||
// Remote cursor mapping (Windows cursor index -> CSS cursor)
|
// Remote cursor mapping (Windows cursor index -> CSS cursor)
|
||||||
// Index matches CursorInfo.h: IDC_APPSTARTING(0) to IDC_WAIT(15), 254=custom, 255=unsupported
|
// 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 = [
|
const cursorMap = [
|
||||||
'progress', // 0: IDC_APPSTARTING
|
'progress', // 0: IDC_APPSTARTING
|
||||||
'default', // 1: IDC_ARROW
|
'default', // 1: IDC_ARROW
|
||||||
'crosshair', // 2: IDC_CROSS
|
'crosshair', // 2: IDC_CROSS
|
||||||
'pointer', // 3: IDC_HAND
|
'pointer', // 3: IDC_HAND
|
||||||
'help', // 4: IDC_HELP
|
'help', // 4: IDC_HELP
|
||||||
'text', // 5: IDC_IBEAM
|
ibeamCursor, // 5: IDC_IBEAM - custom cursor with outline
|
||||||
'default', // 6: IDC_ICON (no direct CSS equivalent)
|
'default', // 6: IDC_ICON (no direct CSS equivalent)
|
||||||
'not-allowed', // 7: IDC_NO
|
'not-allowed', // 7: IDC_NO
|
||||||
'default', // 8: IDC_SIZE (deprecated, use default)
|
'default', // 8: IDC_SIZE (deprecated, use default)
|
||||||
|
|||||||
@@ -1819,3 +1819,7 @@ IOCP
|
|||||||
插件列表为空,无法创建触发器=Plugin list is empty, cannot create trigger
|
插件列表为空,无法创建触发器=Plugin list is empty, cannot create trigger
|
||||||
请先选择至少一个插件=Please select at least one plugin
|
请先选择至少一个插件=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_DOWNLOADS 2235
|
||||||
#define IDT_REMOTE_HOME 2236
|
#define IDT_REMOTE_HOME 2236
|
||||||
#define IDT_REMOTE_SEARCH 2237
|
#define IDT_REMOTE_SEARCH 2237
|
||||||
|
#define IDT_LOCAL_HISTORY 2238
|
||||||
|
#define IDT_REMOTE_HISTORY 2239
|
||||||
#define IDC_BUTTON_SAVE_LICENSE 2240
|
#define IDC_BUTTON_SAVE_LICENSE 2240
|
||||||
#define IDC_LICENSE_LIST 2241
|
#define IDC_LICENSE_LIST 2241
|
||||||
#define IDC_COMBO_VERSION 2245
|
#define IDC_COMBO_VERSION 2245
|
||||||
|
|||||||
Reference in New Issue
Block a user