Add an optional MCP server (JSON-RPC 2.0 over Streamable HTTP) exposing an online-host listing tool, protected by a Bearer token. Disabled by default; configured via a new "Extensions > MCP Settings" dialog. - McpServer: httplib + JSON-RPC 2.0 dispatch (initialize/ping/tools/list/tools/call) - McpSettingsDlg: runtime-created dialog for enable/port/bind/token - HostJson: extract single-host JSON serialization shared with WebService - FRP: expose MCP port (union with listening/Web ports) when bound to 0.0.0.0 - i18n: en_US / zh_TW translations Co-Authored-By: deepseek-v4-pro
118 lines
4.4 KiB
C++
118 lines
4.4 KiB
C++
#include "stdafx.h"
|
||
#include "HostJson.h"
|
||
#include "HostInfo.h" // _ClientList / MAP_NOTE
|
||
#include "context.h" // context 接口 + ONLINELIST_* 枚举
|
||
#include "2015RemoteDlg.h" // GetClientEncoding 辅助函数(UTF-8 能力位判断)
|
||
|
||
#ifndef _WIN64
|
||
#ifdef _DEBUG
|
||
#pragma comment(lib, "jsoncpp/jsoncppd.lib")
|
||
#else
|
||
#pragma comment(lib, "jsoncpp/jsoncpp.lib")
|
||
#endif
|
||
#else
|
||
#ifdef _DEBUG
|
||
#pragma comment(lib, "jsoncpp/jsoncpp_x64d.lib")
|
||
#else
|
||
#pragma comment(lib, "jsoncpp/jsoncpp_x64.lib")
|
||
#endif
|
||
#endif
|
||
|
||
// Helper: Convert ANSI (GBK) string to UTF-8
|
||
// (自 WebService.cpp 抽出,供 BuildDeviceListJson 与 BuildHostJson 共用)
|
||
static std::string AnsiToUtf8(const CString& str) {
|
||
if (str.IsEmpty()) return "";
|
||
#ifdef _UNICODE
|
||
// Unicode build: CString is already UTF-16, convert to UTF-8
|
||
int len = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
|
||
if (len <= 0) return "";
|
||
std::string result(len - 1, '\0');
|
||
WideCharToMultiByte(CP_UTF8, 0, str, -1, &result[0], len, NULL, NULL);
|
||
return result;
|
||
#else
|
||
// MBCS build: CString is ANSI (GBK), need to convert to UTF-16 first, then to UTF-8
|
||
int wlen = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
|
||
if (wlen <= 0) return "";
|
||
std::wstring wstr(wlen - 1, L'\0');
|
||
MultiByteToWideChar(CP_ACP, 0, str, -1, &wstr[0], wlen);
|
||
|
||
int u8len = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
|
||
if (u8len <= 0) return "";
|
||
std::string result(u8len - 1, '\0');
|
||
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &result[0], u8len, NULL, NULL);
|
||
return result;
|
||
#endif
|
||
}
|
||
|
||
Json::Value BuildHostJson(context* ctx, _ClientList* clientMap) {
|
||
Json::Value device;
|
||
|
||
if (!ctx) return device; // 空 context → 返回 null Json::Value
|
||
|
||
// Use string for ID to avoid JavaScript number precision loss
|
||
device["id"] = std::to_string(ctx->GetClientID());
|
||
|
||
CString name = ctx->GetClientData(ONLINELIST_COMPUTER_NAME);
|
||
device["name"] = AnsiToUtf8(name);
|
||
|
||
// 用户在 MFC 端给这台主机起的备注(菜单"修改备注"写入 MAP_NOTE)
|
||
if (clientMap) {
|
||
CString remark = clientMap->GetClientMapData(ctx->GetClientID(), MAP_NOTE);
|
||
if (!remark.IsEmpty()) {
|
||
device["remark"] = AnsiToUtf8(remark);
|
||
}
|
||
}
|
||
|
||
CString ip = ctx->GetClientData(ONLINELIST_IP);
|
||
device["ip"] = AnsiToUtf8(ip);
|
||
|
||
CString os = ctx->GetClientData(ONLINELIST_OS);
|
||
device["os"] = AnsiToUtf8(os);
|
||
|
||
CString location = ctx->GetClientData(ONLINELIST_LOCATION);
|
||
device["location"] = AnsiToUtf8(location);
|
||
|
||
CString rtt = ctx->GetClientData(ONLINELIST_PING);
|
||
device["rtt"] = AnsiToUtf8(rtt);
|
||
|
||
CString version = ctx->GetClientData(ONLINELIST_VERSION);
|
||
device["version"] = AnsiToUtf8(version);
|
||
|
||
// 活动窗口编码由客户端能力位决定:新客户端是 UTF-8,老客户端是 CP_ACP(默认 936)。
|
||
// 不能像其它字段那样无脑 AnsiToUtf8——会把新客户端的 UTF-8 字节再当 GBK 双重编码。
|
||
CString activeWindow = ctx->GetClientData(ONLINELIST_LOGINTIME);
|
||
std::string activeWindowU8;
|
||
if (!activeWindow.IsEmpty()) {
|
||
UINT cp = GetClientEncoding(ctx);
|
||
int wlen = MultiByteToWideChar(cp, 0, activeWindow, -1, NULL, 0);
|
||
if (wlen > 1) {
|
||
std::wstring w(wlen - 1, L'\0');
|
||
MultiByteToWideChar(cp, 0, activeWindow, -1, &w[0], wlen);
|
||
int u8len = WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, NULL, 0, NULL, NULL);
|
||
if (u8len > 1) {
|
||
activeWindowU8.resize(u8len - 1);
|
||
WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, &activeWindowU8[0], u8len, NULL, NULL);
|
||
}
|
||
}
|
||
}
|
||
device["activeWindow"] = activeWindowU8;
|
||
device["online"] = true;
|
||
|
||
// Add device group to response
|
||
std::string deviceGroup = ctx->GetGroupName();
|
||
if (deviceGroup.empty()) deviceGroup = "default";
|
||
device["group"] = deviceGroup;
|
||
|
||
// Get screen info from client's reported resolution
|
||
// Format: "n:MxN" where n=monitor count, M=width, N=height
|
||
CString resolution = ctx->GetAdditionalData(RES_RESOLUTION);
|
||
if (!resolution.IsEmpty()) {
|
||
device["screen"] = AnsiToUtf8(resolution); // e.g. "2:3840x1080"
|
||
}
|
||
|
||
CString clientType = ctx->GetAdditionalData(RES_CLIENT_TYPE);
|
||
device["clientType"] = AnsiToUtf8(clientType); // e.g. "MAC", "LNX", "EXE"
|
||
|
||
return device;
|
||
}
|