Expose the online-list TLS column through list_online_hosts, search_hosts and get_host_detail. The value comes from the flag the client reports at login, so old clients always read false. Add list_network_connections and kill_network_connection on top of the connection manager sub-connection protocol. The kill path matches the original 110-byte record instead of rebuilding addresses, supports a dry run, refuses UDP/IPv6 targets, and verifies that the quadruple really disappeared instead of assuming success. Add a host_* family backed by the machine manager protocol: ten read tools covering every tab of the host management dialog, and four recoverable write tools for process freeze/thaw, window restore/hide/ maximize/minimize, service start/stop/pause/continue/restart, and scheduled task run/stop. Every write re-reads the snapshot before sending and re-checks afterwards, reporting -32009 for stale targets rather than acting blindly. The family is gated by McpMachine, the risky subset by McpMachineDangerous, and writes additionally require read-only mode to be off. Route managed sub-connections from MessageHandle and OfflineProc, keep the single-in-flight rule by sharing the pending table with a request kind, and move the shared JSON/lock/encoding helpers into McpInternal so both tool families use one implementation. Settings dialog, design docs and en_US/zh_TW/de_DE translations are updated accordingly. Co-Authored-By: deepseek-flash
173 lines
5.3 KiB
C++
173 lines
5.3 KiB
C++
#include "stdafx.h"
|
||
#include "McpInternal.h"
|
||
#include "context.h" // context 接口(GetClientID/IsLogin)
|
||
#include "2015RemoteDlg.h" // CMy2015RemoteDlg 成员(m_HostList/m_cs)+ WM_SHOWERRORMSG
|
||
#include "LangManager.h" // _TR
|
||
|
||
#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
|
||
|
||
namespace McpInternal {
|
||
|
||
std::string JsonToString(const Json::Value& v) {
|
||
Json::StreamWriterBuilder b;
|
||
b["indentation"] = "";
|
||
return Json::writeString(b, v);
|
||
}
|
||
|
||
std::string BuildResult(const Json::Value& id, const Json::Value& result) {
|
||
Json::Value resp(Json::objectValue);
|
||
resp["jsonrpc"] = "2.0";
|
||
resp["id"] = id;
|
||
resp["result"] = result;
|
||
return JsonToString(resp);
|
||
}
|
||
|
||
std::string BuildError(const Json::Value& id, int code, const std::string& msg) {
|
||
Json::Value resp(Json::objectValue);
|
||
resp["jsonrpc"] = "2.0";
|
||
resp["id"] = id;
|
||
Json::Value err(Json::objectValue);
|
||
err["code"] = code;
|
||
err["message"] = msg;
|
||
resp["error"] = err;
|
||
return JsonToString(resp);
|
||
}
|
||
|
||
std::string ToLowerAscii(const std::string& s) {
|
||
std::string r = s;
|
||
for (char& c : r) if (c >= 'A' && c <= 'Z') c = (char)(c - 'A' + 'a');
|
||
return r;
|
||
}
|
||
|
||
bool ContainsCI(const std::string& haystack, const std::string& needle) {
|
||
if (needle.empty()) return true;
|
||
return ToLowerAscii(haystack).find(ToLowerAscii(needle)) != std::string::npos;
|
||
}
|
||
|
||
std::string JsonStrField(const Json::Value& v, const char* key) {
|
||
if (v.isObject() && v.isMember(key) && v[key].isString())
|
||
return v[key].asString();
|
||
return "";
|
||
}
|
||
|
||
bool IsDigits(const std::string& s) {
|
||
if (s.empty()) return false;
|
||
for (char c : s) if (c < '0' || c > '9') return false;
|
||
return true;
|
||
}
|
||
|
||
std::string GetStringArg(const Json::Value& args, const char* key) {
|
||
return JsonStrField(args, key);
|
||
}
|
||
|
||
bool GetIntArg(const Json::Value& args, const char* key, int& out) {
|
||
if (!args.isMember(key)) return false;
|
||
const Json::Value& v = args[key];
|
||
if (v.isNumeric()) { out = v.asInt(); return true; }
|
||
if (v.isString()) {
|
||
const std::string s = v.asString();
|
||
if (IsDigits(s) || (!s.empty() && s[0] == '-' && IsDigits(s.substr(1)))) {
|
||
out = atoi(s.c_str());
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool ReadUInt64(const Json::Value& v, uint64_t& out) {
|
||
if (v.isNumeric()) { out = v.asUInt64(); return true; }
|
||
if (v.isString()) {
|
||
std::string s = v.asString();
|
||
if (IsDigits(s)) { out = strtoull(s.c_str(), nullptr, 10); return true; }
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool ReadUInt64Arg(const Json::Value& args, const char* key, uint64_t& out) {
|
||
if (!args.isMember(key)) return false;
|
||
return ReadUInt64(args[key], out);
|
||
}
|
||
|
||
bool ParseHostIdArg(const Json::Value& args, uint64_t& out, std::string& err) {
|
||
std::string sid = GetStringArg(args, "id");
|
||
if (sid.empty()) {
|
||
err = "Missing required parameter: id";
|
||
return false;
|
||
}
|
||
if (!IsDigits(sid)) {
|
||
err = "Invalid id: expected a decimal host id string";
|
||
return false;
|
||
}
|
||
out = strtoull(sid.c_str(), nullptr, 10);
|
||
return true;
|
||
}
|
||
|
||
context* FindMainContext(CMy2015RemoteDlg* parent, uint64_t id) {
|
||
if (!parent) return nullptr;
|
||
context* found = nullptr;
|
||
EnterCriticalSection(&parent->m_cs);
|
||
for (context* ctx : parent->m_HostList) {
|
||
if (ctx && ctx->GetClientID() == id && ctx->IsLogin()) {
|
||
found = ctx;
|
||
break;
|
||
}
|
||
}
|
||
LeaveCriticalSection(&parent->m_cs);
|
||
return found;
|
||
}
|
||
|
||
size_t BoundedStrlen(const char* p, size_t avail) {
|
||
size_t n = 0;
|
||
while (n < avail && p[n]) ++n;
|
||
return n;
|
||
}
|
||
|
||
std::string ToUtf8(const char* s, UINT cp) {
|
||
if (!s || !*s) return "";
|
||
int wlen = MultiByteToWideChar(cp, 0, s, -1, NULL, 0);
|
||
if (wlen <= 0) return "";
|
||
std::wstring w(wlen - 1, L'\0');
|
||
MultiByteToWideChar(cp, 0, s, -1, &w[0], wlen);
|
||
int u8len = WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, NULL, 0, NULL, NULL);
|
||
if (u8len <= 0) return "";
|
||
std::string out(u8len - 1, '\0');
|
||
WideCharToMultiByte(CP_UTF8, 0, w.c_str(), -1, &out[0], u8len, NULL, NULL);
|
||
return out;
|
||
}
|
||
|
||
std::string ToAnsi(const std::string& utf8, UINT cp) {
|
||
if (utf8.empty()) return "";
|
||
int wlen = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), (int)utf8.size(), NULL, 0);
|
||
if (wlen <= 0) return utf8;
|
||
std::wstring w(wlen, L'\0');
|
||
MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), (int)utf8.size(), &w[0], wlen);
|
||
int alen = WideCharToMultiByte(cp, 0, w.c_str(), wlen, NULL, 0, NULL, NULL);
|
||
if (alen <= 0) return utf8;
|
||
std::string out(alen, '\0');
|
||
WideCharToMultiByte(cp, 0, w.c_str(), wlen, &out[0], alen, NULL, NULL);
|
||
return out;
|
||
}
|
||
|
||
void AuditManageAction(CMy2015RemoteDlg* parent, uint64_t devId,
|
||
const std::string& tool, const std::string& detail) {
|
||
if (!parent) return;
|
||
std::string text = "host " + std::to_string(devId) + " " + tool + ": " + detail;
|
||
parent->PostMessageA(WM_SHOWERRORMSG,
|
||
(WPARAM)new CString(ToAnsi(text, 936).c_str()),
|
||
(LPARAM)new CString(_TR("MCP管理操作")));
|
||
}
|
||
|
||
} // namespace McpInternal
|