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
This commit is contained in:
yuanyuanxiang
2026-08-07 11:45:18 +02:00
parent 7bbc47a0ee
commit c8973282cb
5 changed files with 82 additions and 2 deletions

View File

@@ -944,6 +944,7 @@ BEGIN_MESSAGE_MAP(CMy2015RemoteDlg, CDialogEx)
ON_COMMAND(ID_MSGLOG_SEARCH, &CMy2015RemoteDlg::OnMsglogSearch) ON_COMMAND(ID_MSGLOG_SEARCH, &CMy2015RemoteDlg::OnMsglogSearch)
ON_COMMAND(ID_MSGLOG_LOGIN_NOTIFY, &CMy2015RemoteDlg::OnMsglogLoginNotify) ON_COMMAND(ID_MSGLOG_LOGIN_NOTIFY, &CMy2015RemoteDlg::OnMsglogLoginNotify)
ON_COMMAND(ID_MSGLOG_BAN_IP, &CMy2015RemoteDlg::OnMsglogBanIP) 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_ADD_WATCH, &CMy2015RemoteDlg::OnOnlineAddWatch)
ON_COMMAND(ID_ONLINE_LOGIN_NOTIFY, &CMy2015RemoteDlg::OnOnlineLoginNotify) ON_COMMAND(ID_ONLINE_LOGIN_NOTIFY, &CMy2015RemoteDlg::OnOnlineLoginNotify)
ON_NOTIFY(NM_CUSTOMDRAW, IDC_ONLINE, &CMy2015RemoteDlg::OnNMCustomdrawOnline) ON_NOTIFY(NM_CUSTOMDRAW, IDC_ONLINE, &CMy2015RemoteDlg::OnNMCustomdrawOnline)
@@ -9308,13 +9309,15 @@ void CMy2015RemoteDlg::OnRClickMessage(NMHDR* pNMHDR, LRESULT* pResult)
menu.AppendMenu(MF_SEPARATOR); menu.AppendMenu(MF_SEPARATOR);
menu.AppendMenu(MF_STRING, ID_MSGLOG_LOGIN_NOTIFY, _TR("上线提醒")); 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_BAN_IP, _TR("封禁IP"));
menu.AppendMenu(MF_STRING, ID_MSGLOG_UNBAN_IP, _TR("解除封禁"));
// 没有选中项时禁用"删除选中"、"复制选中"、"上线提醒""封禁IP" // 没有选中项时禁用"删除选中"、"复制选中"、"上线提醒""封禁IP"和"解除封禁"
if (m_CList_Message.GetSelectedCount() == 0) { if (m_CList_Message.GetSelectedCount() == 0) {
menu.EnableMenuItem(ID_MSGLOG_DELETE, MF_GRAYED); menu.EnableMenuItem(ID_MSGLOG_DELETE, MF_GRAYED);
menu.EnableMenuItem(ID_MSGLOG_COPY, MF_GRAYED); menu.EnableMenuItem(ID_MSGLOG_COPY, MF_GRAYED);
menu.EnableMenuItem(ID_MSGLOG_LOGIN_NOTIFY, MF_GRAYED); menu.EnableMenuItem(ID_MSGLOG_LOGIN_NOTIFY, MF_GRAYED);
menu.EnableMenuItem(ID_MSGLOG_BAN_IP, MF_GRAYED); menu.EnableMenuItem(ID_MSGLOG_BAN_IP, MF_GRAYED);
menu.EnableMenuItem(ID_MSGLOG_UNBAN_IP, MF_GRAYED);
} }
// PowerShell不可用时禁用"上线提醒" // PowerShell不可用时禁用"上线提醒"
if (!GetNotifyManager().IsPowerShellAvailable()) { if (!GetNotifyManager().IsPowerShellAvailable()) {
@@ -9623,6 +9626,73 @@ void CMy2015RemoteDlg::OnMsglogBanIP()
MessageBoxL(details, _TR("封禁IP"), MB_ICONINFORMATION); 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() void CMy2015RemoteDlg::OnOnlineAddWatch()
{ {
EnterCriticalSection(&m_cs); EnterCriticalSection(&m_cs);

View File

@@ -566,6 +566,7 @@ public:
afx_msg void OnMsglogSearch(); afx_msg void OnMsglogSearch();
afx_msg void OnMsglogLoginNotify(); afx_msg void OnMsglogLoginNotify();
afx_msg void OnMsglogBanIP(); afx_msg void OnMsglogBanIP();
afx_msg void OnMsglogUnbanIP();
afx_msg void OnOnlineAddWatch(); afx_msg void OnOnlineAddWatch();
afx_msg void OnNMCustomdrawOnline(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnNMCustomdrawOnline(NMHDR* pNMHDR, LRESULT* pResult);
afx_msg void OnOnlineRunAsAdmin(); afx_msg void OnOnlineRunAsAdmin();

View File

@@ -1973,3 +1973,7 @@ FRPC Զ
%d 个 IP 已在黑名单中=%d IP(s) already in blacklist %d 个 IP 已在黑名单中=%d IP(s) already in blacklist
%d 个 IP 在白名单中,已跳过=%d IP(s) in whitelist, skipped %d 个 IP 在白名单中,已跳过=%d IP(s) in whitelist, skipped
未执行任何操作=No action taken 未执行任何操作=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

View File

@@ -1964,3 +1964,7 @@ FRPC Զ
%d 个 IP 已在黑名单中=%d 個 IP 已在黑名單中 %d 个 IP 已在黑名单中=%d 個 IP 已在黑名單中
%d 个 IP 在白名单中,已跳过=%d 個 IP 在白名單中,已跳過 %d 个 IP 在白名单中,已跳过=%d 個 IP 在白名單中,已跳過
未执行任何操作=未執行任何操作 未执行任何操作=未執行任何操作
解除封禁=解除封禁
已将 %d 个 IP 从黑名单移除=已將 %d 個 IP 從黑名單移除
已恢复 %d 个客户端的授权状态=已恢復 %d 個客戶端的授權狀態
未找到需要解除封禁的 IP=未找到需要解除封禁的 IP

View File

@@ -993,6 +993,7 @@
#define ID_MSGLOG_SEARCH 33078 #define ID_MSGLOG_SEARCH 33078
#define ID_MSGLOG_LOGIN_NOTIFY 33079 #define ID_MSGLOG_LOGIN_NOTIFY 33079
#define ID_MSGLOG_BAN_IP 33080 #define ID_MSGLOG_BAN_IP 33080
#define ID_MSGLOG_UNBAN_IP 33081
#define ID_WEB_REMOTE_CONTROL 33044 #define ID_WEB_REMOTE_CONTROL 33044
#define ID_TOOL_PLUGIN_SETTINGS 33045 #define ID_TOOL_PLUGIN_SETTINGS 33045
#define ID_33046 33046 #define ID_33046 33046
@@ -1034,7 +1035,7 @@
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 398 #define _APS_NEXT_RESOURCE_VALUE 398
#define _APS_NEXT_COMMAND_VALUE 33081 #define _APS_NEXT_COMMAND_VALUE 33082
#define _APS_NEXT_CONTROL_VALUE 2542 #define _APS_NEXT_CONTROL_VALUE 2542
#define _APS_NEXT_SYMED_VALUE 105 #define _APS_NEXT_SYMED_VALUE 105
#endif #endif