1#include "name_ban.h"
2
3#include <base/log.h>
4#include <base/system.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 m_pConsole = pConsole;
19
20 m_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");
21 m_pConsole->Register(pName: "name_unban", pParams: "s[name]", Flags: CFGFLAG_SERVER, pfnFunc: ConNameUnban, pUser: this, pHelp: "Unban a certain nickname");
22 m_pConsole->Register(pName: "name_bans", pParams: "", Flags: CFGFLAG_SERVER, pfnFunc: ConNameBans, pUser: this, pHelp: "List all name bans");
23}
24
25void CNameBans::Ban(const char *pName, const char *pReason, const int Distance, const bool IsSubstring)
26{
27 for(auto &Ban : m_vNameBans)
28 {
29 if(str_comp(a: Ban.m_aName, b: pName) == 0)
30 {
31 if(m_pConsole)
32 {
33 char aBuf[256];
34 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "changed name='%s' distance=%d old_distance=%d is_substring=%d old_is_substring=%d reason='%s' old_reason='%s'", pName, Distance, Ban.m_Distance, IsSubstring, Ban.m_IsSubstring, pReason, Ban.m_aReason);
35 m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "name_ban", pStr: aBuf);
36 }
37 str_copy(dst&: Ban.m_aReason, src: pReason);
38 Ban.m_Distance = Distance;
39 Ban.m_IsSubstring = IsSubstring;
40 return;
41 }
42 }
43
44 m_vNameBans.emplace_back(args&: pName, args&: pReason, args: Distance, args: IsSubstring);
45 if(m_pConsole)
46 {
47 char aBuf[256];
48 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "added name='%s' distance=%d is_substring=%d reason='%s'", pName, Distance, IsSubstring, pReason);
49 m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "name_ban", pStr: aBuf);
50 }
51}
52
53void CNameBans::Unban(const char *pName)
54{
55 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; });
56 if(ToRemove == m_vNameBans.end())
57 {
58 if(m_pConsole)
59 {
60 char aBuf[256];
61 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "name ban '%s' not found", pName);
62 m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "name_ban", pStr: aBuf);
63 }
64 }
65 else
66 {
67 if(m_pConsole)
68 {
69 char aBuf[256];
70 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "removed name='%s' distance=%d is_substring=%d reason='%s'", (*ToRemove).m_aName, (*ToRemove).m_Distance, (*ToRemove).m_IsSubstring, (*ToRemove).m_aReason);
71 m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "name_ban", pStr: aBuf);
72 }
73 m_vNameBans.erase(first: ToRemove, last: m_vNameBans.end());
74 }
75}
76
77void CNameBans::Dump() const
78{
79 if(!m_pConsole)
80 return;
81
82 char aBuf[256];
83 for(const auto &Ban : m_vNameBans)
84 {
85 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "name='%s' distance=%d is_substring=%d reason='%s'", Ban.m_aName, Ban.m_Distance, Ban.m_IsSubstring, Ban.m_aReason);
86 m_pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "name_ban", pStr: aBuf);
87 }
88}
89
90const CNameBan *CNameBans::IsBanned(const char *pName) const
91{
92 char aTrimmed[MAX_NAME_LENGTH];
93 str_copy(dst&: aTrimmed, src: str_utf8_skip_whitespaces(str: pName));
94 str_utf8_trim_right(param: aTrimmed);
95
96 int aSkeleton[MAX_NAME_SKELETON_LENGTH];
97 int SkeletonLength = str_utf8_to_skeleton(str: aTrimmed, buf: aSkeleton, buf_len: std::size(aSkeleton));
98 int aBuffer[MAX_NAME_SKELETON_LENGTH * 2 + 2];
99
100 const CNameBan *pResult = nullptr;
101 for(const CNameBan &Ban : m_vNameBans)
102 {
103 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));
104 if(Distance <= Ban.m_Distance || (Ban.m_IsSubstring && str_utf8_find_nocase(haystack: pName, needle: Ban.m_aName)))
105 pResult = &Ban;
106 }
107 return pResult;
108}
109
110void CNameBans::ConNameBan(IConsole::IResult *pResult, void *pUser)
111{
112 const char *pName = pResult->GetString(Index: 0);
113 const char *pReason = pResult->NumArguments() > 3 ? pResult->GetString(Index: 3) : "";
114 const int Distance = pResult->NumArguments() > 1 ? pResult->GetInteger(Index: 1) : str_length(str: pName) / 3;
115 const bool IsSubstring = pResult->NumArguments() > 2 ? pResult->GetInteger(Index: 2) != 0 : false;
116 static_cast<CNameBans *>(pUser)->Ban(pName, pReason, Distance, IsSubstring);
117}
118
119void CNameBans::ConNameUnban(IConsole::IResult *pResult, void *pUser)
120{
121 const char *pName = pResult->GetString(Index: 0);
122 static_cast<CNameBans *>(pUser)->Unban(pName);
123}
124
125void CNameBans::ConNameBans(IConsole::IResult *pResult, void *pUser)
126{
127 CNameBans *pThis = static_cast<CNameBans *>(pUser);
128
129 if(pThis->m_vNameBans.empty())
130 {
131 log_info("name_ban", "The name bans list is empty.");
132 }
133 else
134 {
135 pThis->Dump();
136 }
137}
138