1#include "name_ban.h"
2
3#include <base/log.h>
4#include <base/str.h>
5
6#include <engine/shared/config.h>
7
8CNameBan::CNameBan(const char *pName, const char *pReason, int Distance, bool IsSubstring) :
9 m_Distance(Distance), m_IsSubstring(IsSubstring)
10{
11 str_copy(dst&: m_aName, src: pName);
12 str_copy(dst&: m_aReason, src: pReason);
13 m_SkeletonLength = str_utf8_to_skeleton(str: m_aName, buf: m_aSkeleton, buf_len: std::size(m_aSkeleton));
14}
15
16void CNameBans::InitConsole(IConsole *pConsole)
17{
18 pConsole->Register(pName: "name_ban", pParams: "s[name] ?i[distance] ?i[is_substring] ?r[reason]", Flags: CFGFLAG_SERVER, pfnFunc: ConNameBan, pUser: this, pHelp: "Ban a certain nickname");
19 pConsole->Register(pName: "name_unban", pParams: "s[name]", Flags: CFGFLAG_SERVER, pfnFunc: ConNameUnban, pUser: this, pHelp: "Unban a certain nickname");
20 pConsole->Register(pName: "name_bans", pParams: "", Flags: CFGFLAG_SERVER, pfnFunc: ConNameBans, pUser: this, pHelp: "List all name bans");
21}
22
23void CNameBans::Ban(const char *pName, const char *pReason, const int Distance, const bool IsSubstring)
24{
25 for(auto &Ban : m_vNameBans)
26 {
27 if(str_comp(a: Ban.m_aName, b: pName) == 0)
28 {
29 log_info("name_ban", "changed name='%s' distance=%d old_distance=%d is_substring=%d old_is_substring=%d reason='%s' old_reason='%s'",
30 pName, Distance, Ban.m_Distance, IsSubstring, Ban.m_IsSubstring, pReason, Ban.m_aReason);
31 str_copy(dst&: Ban.m_aReason, src: pReason);
32 Ban.m_Distance = Distance;
33 Ban.m_IsSubstring = IsSubstring;
34 return;
35 }
36 }
37
38 m_vNameBans.emplace_back(args&: pName, args&: pReason, args: Distance, args: IsSubstring);
39 log_info("name_ban", "added name='%s' distance=%d is_substring=%d reason='%s'",
40 pName, Distance, IsSubstring, pReason);
41}
42
43void CNameBans::Unban(const char *pName)
44{
45 auto ToRemove = std::remove_if(first: m_vNameBans.begin(), last: m_vNameBans.end(), pred: [pName](const CNameBan &Ban) { return str_comp(a: Ban.m_aName, b: pName) == 0; });
46 if(ToRemove == m_vNameBans.end())
47 {
48 log_error("name_ban", "name ban '%s' not found", pName);
49 }
50 else
51 {
52 log_info("name_ban", "removed name='%s' distance=%d is_substring=%d reason='%s'",
53 (*ToRemove).m_aName, (*ToRemove).m_Distance, (*ToRemove).m_IsSubstring, (*ToRemove).m_aReason);
54 m_vNameBans.erase(first: ToRemove, last: m_vNameBans.end());
55 }
56}
57
58void CNameBans::Dump() const
59{
60 for(const auto &Ban : m_vNameBans)
61 {
62 log_info("name_ban", "name='%s' distance=%d is_substring=%d reason='%s'",
63 Ban.m_aName, Ban.m_Distance, Ban.m_IsSubstring, Ban.m_aReason);
64 }
65}
66
67const CNameBan *CNameBans::IsBanned(const char *pName) const
68{
69 char aTrimmed[MAX_NAME_LENGTH];
70 str_copy(dst&: aTrimmed, src: str_utf8_skip_whitespaces(str: pName));
71 str_utf8_trim_right(param: aTrimmed);
72
73 int aSkeleton[MAX_NAME_SKELETON_LENGTH];
74 int SkeletonLength = str_utf8_to_skeleton(str: aTrimmed, buf: aSkeleton, buf_len: std::size(aSkeleton));
75 int aBuffer[MAX_NAME_SKELETON_LENGTH * 2 + 2];
76
77 const CNameBan *pResult = nullptr;
78 for(const CNameBan &Ban : m_vNameBans)
79 {
80 int Distance = str_utf32_dist_buffer(a: aSkeleton, a_len: SkeletonLength, b: Ban.m_aSkeleton, b_len: Ban.m_SkeletonLength, buf: aBuffer, buf_len: std::size(aBuffer));
81 if(Distance <= Ban.m_Distance || (Ban.m_IsSubstring && str_utf8_find_nocase(haystack: pName, needle: Ban.m_aName)))
82 pResult = &Ban;
83 }
84 return pResult;
85}
86
87void CNameBans::ConNameBan(IConsole::IResult *pResult, void *pUser)
88{
89 const char *pName = pResult->GetString(Index: 0);
90 const char *pReason = pResult->NumArguments() > 3 ? pResult->GetString(Index: 3) : "";
91 const int Distance = pResult->NumArguments() > 1 ? pResult->GetInteger(Index: 1) : str_length(str: pName) / 3;
92 const bool IsSubstring = pResult->NumArguments() > 2 ? pResult->GetInteger(Index: 2) != 0 : false;
93 static_cast<CNameBans *>(pUser)->Ban(pName, pReason, Distance, IsSubstring);
94}
95
96void CNameBans::ConNameUnban(IConsole::IResult *pResult, void *pUser)
97{
98 const char *pName = pResult->GetString(Index: 0);
99 static_cast<CNameBans *>(pUser)->Unban(pName);
100}
101
102void CNameBans::ConNameBans(IConsole::IResult *pResult, void *pUser)
103{
104 CNameBans *pThis = static_cast<CNameBans *>(pUser);
105
106 if(pThis->m_vNameBans.empty())
107 {
108 log_info("name_ban", "The name bans list is empty.");
109 }
110 else
111 {
112 pThis->Dump();
113 }
114}
115