4 Commits
Author SHA1 Message Date
yuanyuanxiangandDeepSeek V4 Pro c8973282cb Feature: Add "Unban IP" to log message context menu in main dialog
- Add "Unban IP" menu item to the log message list (m_CList_Message)
  right-click context menu, performing two actions on the extracted IP:
  1. Remove the IP from the IPBlacklist singleton and persist to config
  2. Scan the entire history host list (m_ClientMap->GetAll()) for all
     clients matching the IP with AUTH_FORBIDDEN status, and restore
     them to UNAUTHORIZED (0) — requiring re-authorization for safety
- Reuses ExtractFirstIPFromMessage() for dependency-free IPv4 parsing
- Handles the case where an IP is not in the blacklist and has no
  matching forbidden clients with a clear "no action needed" message
- Menu item is grayed out when no log entry is selected
- Sync en_US / zh_TW language files with new translation strings

Co-Authored-By: DeepSeek V4 Pro
2026-08-07 11:45:18 +02:00
yuanyuanxiangandDeepSeek V4 Pro 7bbc47a0ee Feature: Add "Ban IP" to log message context menu in main dialog
- Add "Ban IP" menu item to the log message list (m_CList_Message)
  right-click context menu, allowing users to extract IPv4 addresses from
  selected log entries and add them directly to the IP blacklist
- Reuses ExtractFirstIPFromMessage() from the previous commit for
  dependency-free IPv4 parsing with per-segment 0-255 validation
- Checks against whitelist before banning (whitelisted IPs cannot be
  banned) and skips IPs already in blacklist with clear feedback
- Immediately persists via THIS_CFG.SetStr to the config ini file;
  IPBlacklist singleton takes effect instantly — subsequent connections
  from banned IPs are rejected in IOCPServer::OnAccept() which already
  calls IsIPBlacklisted() on every new connection
- Menu item is grayed out when no log entry is selected
- Sync en_US / zh_TW language files with new translation strings

Co-Authored-By: DeepSeek V4 Pro
2026-08-06 20:44:33 +02:00
yuanyuanxiangandDeepSeek V4 Pro eeacbe5d61 Feature: Add "Online Notify" to log message context menu in main dialog
- Add "Online Notify" menu item to the log message list (m_CList_Message)
  right-click context menu, allowing users to extract IPv4 addresses from
  selected log entries and add them to notification keywords
- Implement ExtractFirstIPFromMessage() for dependency-free IPv4 parsing
  with per-segment 0-255 validation, handling diverse log message formats
- Extend NotifyManager::ShouldNotify with IP column (ONLINELIST_IP)
  fallback check, ensuring IP keywords match regardless of the user's
  configured ColumnIndex setting
- OnMsglogLoginNotify only appends keywords and enables the rule without
  overwriting the user's existing ColumnIndex or TriggerType settings
- Menu item is automatically grayed out when PowerShell is unavailable
  or no log entry is selected
- Sync en_US / zh_TW language files with new translation strings

Co-Authored-By: DeepSeek V4 Pro
2026-08-06 18:13:14 +02:00
yuanyuanxiangandDeepSeek V4 Pro 7c09e9dd8c Fix: Move ONLINELIST_PRIVILEGE to end of enum to fix history host column data corruption
Commit 6d0b1bb (Feat: Add privilege column) inserted ONLINELIST_PRIVILEGE
at enum value 9, shifting INSTALLTIME from 9→10 and PATH from 12→13.
However, FileUpload_Lib (SimplePlugins) is compiled against a separate
copy of context.h that still uses the old enum values. Since
HostInfo.cpp's SaveClientMapData uses OLD enum values to read
sClientInfo[], it was reading privilegeStr for InstallTime and client
type for ProgramPath — corrupting the history host database.

Fix: move ONLINELIST_PRIVILEGE to value 16 (just before MAX), restoring
all original enum values (INSTALLTIME=9, PATH=12, etc.) so the
pre-built library can continue reading correct data without recompilation.

The 'permission' column now appears as the rightmost column in the
online host list instead of between 'version' and 'install time'.

Co-Authored-By: DeepSeek V4 Pro
2026-08-05 20:07:21 +02:00
9 changed files with 408 additions and 33 deletions

No files matched your search

+328 -4
View File
@@ -148,10 +148,14 @@ COLUMNSTRUCT g_Column_Data_Online[g_Column_Count_Online] = {
{"摄像头", 60 },
{"RTT", 60 },
{"版本", 90 },
{"权限", 60 },
{"安装时间", 130 },
{"活动窗口", 130 },
{"类型", 50 },
{"", 0 },
{"", 0 },
{"", 0 },
{"", 0 },
{"权限", 60 },
};
// 缩略图列号常量 + 列号→数据槽映射。负返回值代表"该列无数据槽"。
@@ -938,6 +942,9 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
ON_COMMAND(ID_MSGLOG_COPY, &CMy2015RemoteDlg::OnMsglogCopy)
ON_COMMAND(ID_MSGLOG_CLEAR, &CMy2015RemoteDlg::OnMsglogClear)
ON_COMMAND(ID_MSGLOG_SEARCH, &CMy2015RemoteDlg::OnMsglogSearch)
ON_COMMAND(ID_MSGLOG_LOGIN_NOTIFY, &CMy2015RemoteDlg::OnMsglogLoginNotify)
ON_COMMAND(ID_MSGLOG_BAN_IP, &CMy2015RemoteDlg::OnMsglogBanIP)
ON_COMMAND(ID_MSGLOG_UNBAN_IP, &CMy2015RemoteDlg::OnMsglogUnbanIP)
ON_COMMAND(ID_ONLINE_ADD_WATCH, &CMy2015RemoteDlg::OnOnlineAddWatch)
ON_COMMAND(ID_ONLINE_LOGIN_NOTIFY, &CMy2015RemoteDlg::OnOnlineLoginNotify)
ON_NOTIFY(NM_CUSTOMDRAW, IDC_ONLINE, &CMy2015RemoteDlg::OnNMCustomdrawOnline)
@@ -1412,6 +1419,12 @@ VOID CMy2015RemoteDlg::InitControl()
g_Column_Online_Width+=g_Column_Data_Online[i].nWidth;
}
m_CList_Online.InitColumns();
// 隐藏空标题列(内部数据槽,不允许用户拖拽显示)
for (int i = 0; i < g_Column_Count_Online; ++i) {
if (g_Column_Data_Online[i].szTitle[0] == '\0') {
m_CList_Online.SetColumnVisible(i, FALSE);
}
}
// 让虚拟列表用 Unicode 通知(LVN_GETDISPINFOW),从而绕开 MBCS 工程里
// 列表 → ANSI → CP_ACP 的回转,这样在德语/日语等非中文 ACP 服务端上也能显示中文。
m_CList_Online.SendMessage(LVM_SETUNICODEFORMAT, TRUE, 0);
@@ -1487,8 +1500,8 @@ VOID CMy2015RemoteDlg::AddList(CString strIP, CString strAddr, CString strPCName
CString privilegeStr = privUser == "SYSTEM" ? "SYSTEM" : privAdmin == "1" ? "Admin" : "User";
CString data[ONLINELIST_MAX] = { strIP, strAddr, "", strPCName, strOS, strCPU, strVideo, strPing,
verDisplay, privilegeStr, install, startTime, v[RES_CLIENT_TYPE].empty() ? "?" : v[RES_CLIENT_TYPE].c_str(), path,
v[RES_CLIENT_PUBIP].empty() ? strIP : v[RES_CLIENT_PUBIP].c_str(), startTime, capStr,
verDisplay, install, startTime, v[RES_CLIENT_TYPE].empty() ? "?" : v[RES_CLIENT_TYPE].c_str(), path,
v[RES_CLIENT_PUBIP].empty() ? strIP : v[RES_CLIENT_PUBIP].c_str(), startTime, capStr, privilegeStr,
};
// 优先采用客户端自报的 ID(新客户端用 V2 算法 = MachineGuid + 归一化路径,
// 比服务端按老算法 IP+PC+OS+CPU+PATH 重算更稳定)。
@@ -9293,11 +9306,22 @@ void CMy2015RemoteDlg::OnRClickMessage(NMHDR* pNMHDR, LRESULT* pResult)
menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_STRING, ID_MSGLOG_SEARCH,
m_bLogSearchVisible ? _TR("隐藏搜索") : _TR("搜索日志"));
menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_STRING, ID_MSGLOG_LOGIN_NOTIFY, _TR("上线提醒"));
menu.AppendMenu(MF_STRING, ID_MSGLOG_BAN_IP, _TR("封禁IP"));
menu.AppendMenu(MF_STRING, ID_MSGLOG_UNBAN_IP, _TR("解除封禁"));
// 没有选中项时禁用"删除选中"
// 没有选中项时禁用"删除选中"、"复制选中"、"上线提醒"、"封禁IP"和"解除封禁"
if (m_CList_Message.GetSelectedCount() == 0) {
menu.EnableMenuItem(ID_MSGLOG_DELETE, MF_GRAYED);
menu.EnableMenuItem(ID_MSGLOG_COPY, MF_GRAYED);
menu.EnableMenuItem(ID_MSGLOG_LOGIN_NOTIFY, MF_GRAYED);
menu.EnableMenuItem(ID_MSGLOG_BAN_IP, MF_GRAYED);
menu.EnableMenuItem(ID_MSGLOG_UNBAN_IP, MF_GRAYED);
}
// PowerShell不可用时禁用"上线提醒"
if (!GetNotifyManager().IsPowerShellAvailable()) {
menu.EnableMenuItem(ID_MSGLOG_LOGIN_NOTIFY, MF_GRAYED);
}
// 列表为空时禁用"清空日志"
if (m_CList_Message.GetItemCount() == 0) {
@@ -9369,6 +9393,306 @@ void CMy2015RemoteDlg::OnMsglogSearch()
OnSize(SIZE_RESTORED, rc.Width(), rc.Height());
}
// Extract the first valid IPv4 address from a log message text.
// Returns true and sets outIP if a valid IPv4 address is found.
static bool ExtractFirstIPFromMessage(const CString& msg, CString& outIP)
{
// Scan the string for patterns like d.d.d.d and validate each segment
int len = msg.GetLength();
for (int i = 0; i < len; i++) {
TCHAR ch = msg[i];
// Look for start of a potential IP: digit
if (ch >= _T('0') && ch <= _T('9')) {
int segments[4] = { -1, -1, -1, -1 };
int segIdx = 0;
int numStart = i;
for (int j = i; j < len && segIdx < 4; j++) {
TCHAR c = msg[j];
if (c >= _T('0') && c <= _T('9')) {
if (numStart < 0) numStart = j;
} else if (c == _T('.')) {
if (numStart < 0) break; // consecutive dots or leading dot
// Parse the number
CString numStr = msg.Mid(numStart, j - numStart);
if (numStr.GetLength() > 3) break; // too many digits
int val = _tstoi(numStr);
if (val > 255) break;
segments[segIdx] = val;
segIdx++;
numStart = -1;
if (segIdx >= 4) break; // too many segments
} else {
break; // non-IP character
}
}
// Check if we have exactly 4 valid segments and the last char ends a number
if (segIdx == 3 && numStart >= 0) {
// Find where the number ends
int numEnd = numStart;
while (numEnd < len && msg[numEnd] >= _T('0') && msg[numEnd] <= _T('9'))
numEnd++;
CString numStr = msg.Mid(numStart, numEnd - numStart);
if (numStr.GetLength() <= 3) {
int val = _tstoi(numStr);
if (val >= 0 && val <= 255) {
segments[3] = val;
// Success: build the IP string
outIP.Format(_T("%d.%d.%d.%d"), segments[0], segments[1], segments[2], segments[3]);
return true;
}
}
}
// Skip past the partial match to continue scanning
// (the outer loop will increment i, which is fine)
}
}
return false;
}
void CMy2015RemoteDlg::OnMsglogLoginNotify()
{
// Get selected log messages and extract IP addresses from them
std::vector<std::string> ipsToAdd;
POSITION pos = m_CList_Message.GetFirstSelectedItemPosition();
while (pos) {
int row = m_CList_Message.GetNextSelectedItem(pos);
// Column 2 is the message content ("信息内容")
CString msgText = m_CList_Message.GetItemText(row, 2);
CString extractedIP;
if (ExtractFirstIPFromMessage(msgText, extractedIP)) {
std::string ipUtf8 = CT2A(extractedIP, CP_UTF8);
ipsToAdd.push_back(ipUtf8);
}
}
if (ipsToAdd.empty()) {
MessageBoxL("所选日志消息中未检测到有效的 IP 地址", "提示", MB_ICONINFORMATION);
return;
}
// Helper to convert string to lowercase
auto toLower = [](const std::string& str) -> std::string {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
return result;
};
// Use the same deduplication logic as OnOnlineLoginNotify
NotifyConfig config = GetNotifyManager().GetConfig();
NotifyRule& rule = config.GetRule();
// Parse existing keywords into a set for deduplication (case-insensitive)
std::set<std::string> existingKeywordsLower;
std::vector<std::string> existingKeywords;
std::string pattern = rule.matchPattern;
size_t patternPos = 0;
while ((patternPos = pattern.find(';')) != std::string::npos) {
std::string kw = pattern.substr(0, patternPos);
size_t start = kw.find_first_not_of(" \t");
size_t end = kw.find_last_not_of(" \t");
if (start != std::string::npos) {
std::string trimmed = kw.substr(start, end - start + 1);
existingKeywordsLower.insert(toLower(trimmed));
existingKeywords.push_back(trimmed);
}
pattern.erase(0, patternPos + 1);
}
if (!pattern.empty()) {
size_t start = pattern.find_first_not_of(" \t");
size_t end = pattern.find_last_not_of(" \t");
if (start != std::string::npos) {
std::string trimmed = pattern.substr(start, end - start + 1);
existingKeywordsLower.insert(toLower(trimmed));
existingKeywords.push_back(trimmed);
}
}
// Add new IPs if not already present
int addedCount = 0;
for (const auto& ip : ipsToAdd) {
if (existingKeywordsLower.find(toLower(ip)) == existingKeywordsLower.end()) {
existingKeywordsLower.insert(toLower(ip));
existingKeywords.push_back(ip);
addedCount++;
}
}
if (addedCount == 0) {
MessageBoxL("所有解析出的 IP 已在上线提醒列表中", "提示", MB_ICONINFORMATION);
return;
}
// Rebuild match pattern
std::string newPattern;
for (const auto& kw : existingKeywords) {
if (!newPattern.empty()) {
newPattern += ";";
}
newPattern += kw;
}
// Update config (preserve existing columnIndex and triggerType)
rule.matchPattern = newPattern;
rule.enabled = true;
if (rule.triggerType == NOTIFY_TRIGGER_NONE) {
rule.triggerType = NOTIFY_TRIGGER_HOST_ONLINE;
}
GetNotifyManager().SetConfig(config);
GetNotifyManager().SaveConfig();
// Build message with SMTP warning if not configured
CString msg;
msg.Format(_TR("已添加 %d 个 IP 到上线提醒列表"), addedCount);
if (!config.smtp.IsValid()) {
msg += _T("\n\n");
msg += _TR("注意: SMTP 未配置,请先在通知设置中配置邮箱");
}
MessageBoxL(msg, _TR("上线提醒"), MB_ICONINFORMATION);
}
void CMy2015RemoteDlg::OnMsglogBanIP()
{
// Get selected log messages and extract IP addresses from them
std::vector<std::string> ipsToAdd;
POSITION pos = m_CList_Message.GetFirstSelectedItemPosition();
while (pos) {
int row = m_CList_Message.GetNextSelectedItem(pos);
// Column 2 is the message content ("信息内容")
CString msgText = m_CList_Message.GetItemText(row, 2);
CString extractedIP;
if (ExtractFirstIPFromMessage(msgText, extractedIP)) {
std::string ipUtf8 = CT2A(extractedIP, CP_UTF8);
ipsToAdd.push_back(ipUtf8);
}
}
if (ipsToAdd.empty()) {
MessageBoxL("所选日志消息中未检测到有效的 IP 地址", "提示", MB_ICONINFORMATION);
return;
}
// Process each extracted IP
int bannedCount = 0;
int alreadyCount = 0;
int whitelistCount = 0;
for (const auto& ip : ipsToAdd) {
// Cannot ban whitelisted IPs
if (IPWhitelist::getInstance().IsWhitelisted(ip)) {
whitelistCount++;
continue;
}
// Already in blacklist
if (IPBlacklist::getInstance().IsBlacklisted(ip)) {
alreadyCount++;
continue;
}
IPBlacklist::getInstance().AddIP(ip);
bannedCount++;
}
// Persist to config
if (bannedCount > 0) {
std::string blacklistStr = IPBlacklist::getInstance().Export();
THIS_CFG.SetStr("settings", "IPBlacklist", blacklistStr.c_str());
}
// Build result message
CString details;
if (bannedCount > 0) {
details.AppendFormat(_TR("已封禁 %d 个 IP"), bannedCount);
}
if (alreadyCount > 0) {
if (!details.IsEmpty()) details += _T("\n");
details.AppendFormat(_TR("%d 个 IP 已在黑名单中"), alreadyCount);
}
if (whitelistCount > 0) {
if (!details.IsEmpty()) details += _T("\n");
details.AppendFormat(_TR("%d 个 IP 在白名单中,已跳过"), whitelistCount);
}
if (details.IsEmpty()) {
details = _TR("未执行任何操作");
}
MessageBoxL(details, _TR("封禁IP"), MB_ICONINFORMATION);
}
void CMy2015RemoteDlg::OnMsglogUnbanIP()
{
// Get selected log messages and extract IP addresses from them
std::vector<std::string> ipsToProcess;
POSITION pos = m_CList_Message.GetFirstSelectedItemPosition();
while (pos) {
int row = m_CList_Message.GetNextSelectedItem(pos);
CString msgText = m_CList_Message.GetItemText(row, 2);
CString extractedIP;
if (ExtractFirstIPFromMessage(msgText, extractedIP)) {
std::string ipUtf8 = CT2A(extractedIP, CP_UTF8);
ipsToProcess.push_back(ipUtf8);
}
}
if (ipsToProcess.empty()) {
MessageBoxL("所选日志消息中未检测到有效的 IP 地址", "提示", MB_ICONINFORMATION);
return;
}
int unblacklistedCount = 0;
int clientRestoredCount = 0;
for (const auto& ip : ipsToProcess) {
// 1. Remove from IP blacklist
if (IPBlacklist::getInstance().IsBlacklisted(ip)) {
IPBlacklist::getInstance().RemoveIP(ip);
unblacklistedCount++;
}
// 2. Find all clients matching this IP and restore AUTH_FORBIDDEN to UNAUTHORIZED
for (const auto& kv : m_ClientMap->GetAll()) {
if (strcmp(kv.second.IP, ip.c_str()) == 0
&& kv.second.Authorized == AUTH_FORBIDDEN) {
m_ClientMap->SetClientMapInteger(kv.first, MAP_AUTH, UNAUTHORIZED);
clientRestoredCount++;
}
}
}
// Persist
if (unblacklistedCount > 0) {
std::string blacklistStr = IPBlacklist::getInstance().Export();
THIS_CFG.SetStr("settings", "IPBlacklist", blacklistStr.c_str());
}
if (clientRestoredCount > 0) {
m_ClientMap->SaveToFile(GetDbPath());
}
// Build result message
CString details;
if (unblacklistedCount > 0) {
details.AppendFormat(_TR("已将 %d 个 IP 从黑名单移除"), unblacklistedCount);
}
if (clientRestoredCount > 0) {
if (!details.IsEmpty()) details += _T("\n");
details.AppendFormat(_TR("已恢复 %d 个客户端的授权状态"), clientRestoredCount);
}
if (unblacklistedCount == 0 && clientRestoredCount == 0) {
details = _TR("未找到需要解除封禁的 IP");
}
MessageBoxL(details, _TR("解除封禁"), MB_ICONINFORMATION);
}
void CMy2015RemoteDlg::OnOnlineAddWatch()
{
EnterCriticalSection(&m_cs);
+3
View File
@@ -564,6 +564,9 @@ public:
afx_msg void OnMsglogCopy();
afx_msg void OnMsglogClear();
afx_msg void OnMsglogSearch();
afx_msg void OnMsglogLoginNotify();
afx_msg void OnMsglogBanIP();
afx_msg void OnMsglogUnbanIP();
afx_msg void OnOnlineAddWatch();
afx_msg void OnNMCustomdrawOnline(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnOnlineRunAsAdmin();
+8 -4
View File
@@ -104,10 +104,14 @@ inline const char* GetColumnName(int index) {
"Camera", // 6
"RTT", // 7
"Version", // 8
"Privilege", // 9 ONLINELIST_PRIVILEGE
"InstallTime", // 10 ONLINELIST_INSTALLTIME
"ActiveWindow", // 11 ONLINELIST_LOGINTIME
"ClientType", // 12 ONLINELIST_CLIENTTYPE
"InstallTime", // 9 ONLINELIST_INSTALLTIME
"ActiveWindow", // 10 ONLINELIST_LOGINTIME
"ClientType", // 11 ONLINELIST_CLIENTTYPE
"Path", // 12 ONLINELIST_PATH
"PublicIP", // 13 ONLINELIST_PUBIP
"StartTime", // 14 ONLINELIST_STARTTIME
"Capabilities", // 15 ONLINELIST_CAPABILITIES
"Privilege", // 16 ONLINELIST_PRIVILEGE
};
if (index >= 0 && index < sizeof(names)/sizeof(names[0])) {
return names[index];
+39 -22
View File
@@ -141,35 +141,52 @@ bool NotifyManager::ShouldNotify(context* ctx, std::string& outMatchedKeyword, c
}
}
// Get column text (for COMPUTER_NAME column, prefer remark if available)
// Split pattern by semicolon and get keywords
std::vector<std::string> keywords = SplitString(rule.matchPattern, ';');
// Helper: check if any keyword matches a given column text
auto matchColumn = [&keywords, this](const CString& colText) -> std::string {
if (colText.IsEmpty()) return "";
std::string colTextStr = CT2A(colText, CP_UTF8);
std::string colLower = colTextStr;
std::transform(colLower.begin(), colLower.end(), colLower.begin(), ::tolower);
for (const auto& kw : keywords) {
std::string trimmed = Trim(kw);
if (trimmed.empty()) continue;
std::string kwLower = trimmed;
std::transform(kwLower.begin(), kwLower.end(), kwLower.begin(), ::tolower);
if (colLower.find(kwLower) != std::string::npos) {
return trimmed;
}
}
return "";
};
// Check primary column (for COMPUTER_NAME column, prefer remark if available)
CString colText;
if (rule.columnIndex == ONLINELIST_COMPUTER_NAME && !remark.IsEmpty()) {
colText = remark;
} else {
colText = ctx->GetClientData(rule.columnIndex);
}
if (colText.IsEmpty()) return false;
std::string matched = matchColumn(colText);
if (!matched.empty()) {
outMatchedKeyword = matched;
m_config.lastNotifyTime[clientId] = now;
return true;
}
// Convert to std::string for matching
std::string colTextStr = CT2A(colText, CP_UTF8);
// Split pattern by semicolon and check each keyword
std::vector<std::string> keywords = SplitString(rule.matchPattern, ';');
for (const auto& kw : keywords) {
std::string trimmed = Trim(kw);
if (trimmed.empty()) continue;
// Case-insensitive substring search
std::string colLower = colTextStr;
std::string kwLower = trimmed;
std::transform(colLower.begin(), colLower.end(), colLower.begin(), ::tolower);
std::transform(kwLower.begin(), kwLower.end(), kwLower.begin(), ::tolower);
if (colLower.find(kwLower) != std::string::npos) {
outMatchedKeyword = trimmed;
m_config.lastNotifyTime[clientId] = now;
return true;
}
// Also check the IP column (ONLINELIST_IP) so IP keywords added from log
// messages can match against host IP addresses when they come online.
CString ipText = ctx->GetClientData(ONLINELIST_IP);
matched = matchColumn(ipText);
if (!matched.empty()) {
outMatchedKeyword = matched;
m_config.lastNotifyTime[clientId] = now;
return true;
}
return false;
+1 -1
View File
@@ -111,10 +111,10 @@ void NotifySettingsDlg::PopulateComboBoxes()
{ ONLINELIST_VIDEO, _T("摄像头") },
{ ONLINELIST_PING, _T("延迟") },
{ ONLINELIST_VERSION, _T("版本") },
{ ONLINELIST_PRIVILEGE, _T("权限") },
{ ONLINELIST_INSTALLTIME, _T("安装时间") },
{ ONLINELIST_LOGINTIME, _T("活动窗口") },
{ ONLINELIST_CLIENTTYPE, _T("客户端类型") },
{ ONLINELIST_PRIVILEGE, _T("权限") },
};
for (const auto& col : columns) {
CString item;
+1 -1
View File
@@ -14,7 +14,6 @@ enum {
ONLINELIST_VIDEO, // 摄像头(有无)
ONLINELIST_PING, // PING(对方的网速)
ONLINELIST_VERSION, // 版本信息
ONLINELIST_PRIVILEGE, // 权限级别 (SYSTEM/Admin/User)
ONLINELIST_INSTALLTIME, // 安装时间
ONLINELIST_LOGINTIME, // 活动窗口
ONLINELIST_CLIENTTYPE, // 客户端类型
@@ -22,6 +21,7 @@ enum {
ONLINELIST_PUBIP,
ONLINELIST_STARTTIME,
ONLINELIST_CAPABILITIES, // 客户端能力位
ONLINELIST_PRIVILEGE, // 权限级别 (SYSTEM/Admin/User)
ONLINELIST_MAX,
};
+12
View File
@@ -1965,3 +1965,15 @@ FRPC Զ
搜索日志=Search Logs
隐藏搜索=Hide Search
请=Please
所选日志消息中未检测到有效的 IP 地址=No valid IP address detected in the selected log messages
所有解析出的 IP 已在上线提醒列表中=All extracted IPs are already in the login notify list
已添加 %d 个 IP 到上线提醒列表=Added %d IP(s) to login notify list
封禁IP=Ban IP
已封禁 %d 个 IP=Banned %d IP(s)
%d 个 IP 已在黑名单中=%d IP(s) already in blacklist
%d 个 IP 在白名单中,已跳过=%d IP(s) in whitelist, skipped
未执行任何操作=No action taken
解除封禁=Unban IP
已将 %d 个 IP 从黑名单移除=Removed %d IP(s) from blacklist
已恢复 %d 个客户端的授权状态=Restored authorization for %d client(s)
未找到需要解除封禁的 IP=No IPs found that need unbanning
+12
View File
@@ -1956,3 +1956,15 @@ FRPC Զ
搜索日志=搜索日志
隐藏搜索=隐藏搜索
请=请
所有解析出的 IP 已在上线提醒列表中=所有解析出的 IP 已在上線提醒列表中
已添加 %d 个 IP 到上线提醒列表=已新增 %d 個 IP 到上線提醒列表
所选日志消息中未检测到有效的 IP 地址=所選日誌消息中未檢測到有效的 IP 位址
封禁IP=封禁IP
已封禁 %d 个 IP=已封禁 %d 個 IP
%d 个 IP 已在黑名单中=%d 個 IP 已在黑名單中
%d 个 IP 在白名单中,已跳过=%d 個 IP 在白名單中,已跳過
未执行任何操作=未執行任何操作
解除封禁=解除封禁
已将 %d 个 IP 从黑名单移除=已將 %d 個 IP 從黑名單移除
已恢复 %d 个客户端的授权状态=已恢復 %d 個客戶端的授權狀態
未找到需要解除封禁的 IP=未找到需要解除封禁的 IP
+4 -1
View File
@@ -991,6 +991,9 @@
#define ID_CANCEL_SHARE 33042
#define ID_MSGLOG_COPY 33043
#define ID_MSGLOG_SEARCH 33078
#define ID_MSGLOG_LOGIN_NOTIFY 33079
#define ID_MSGLOG_BAN_IP 33080
#define ID_MSGLOG_UNBAN_IP 33081
#define ID_WEB_REMOTE_CONTROL 33044
#define ID_TOOL_PLUGIN_SETTINGS 33045
#define ID_33046 33046
@@ -1032,7 +1035,7 @@
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 398
#define _APS_NEXT_COMMAND_VALUE 33079
#define _APS_NEXT_COMMAND_VALUE 33082
#define _APS_NEXT_CONTROL_VALUE 2542
#define _APS_NEXT_SYMED_VALUE 105
#endif