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