summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nameblocker.sp199
1 files changed, 182 insertions, 17 deletions
diff --git a/nameblocker.sp b/nameblocker.sp
index 9190dd2..aec6963 100644
--- a/nameblocker.sp
+++ b/nameblocker.sp
@@ -11,24 +11,64 @@ public Plugin myinfo = {
11 url = "git.dabikers.online/smnameblocker" 11 url = "git.dabikers.online/smnameblocker"
12}; 12};
13 13
14#define HANDLE_SIZE 32 14#define HANDLE_SIZE (32)
15// This exists because you can't sizeof() a Handle, but it IS specified to be a 32bit integer. This should also equal the size of 15// This exists because you can't sizeof() a Handle, but it IS specified to be a 32bit integer. This should also equal the size of
16// any other methodmap or descendant of Handle (like Regex) 16// any other methodmap or descendant of Handle (like Regex)
17 17
18#define PATTERN_MAX_LEN (512 + 1) /* 512 chars + null terminator */
19
18enum OperatingMode { 20enum OperatingMode {
19 OP_DISABLED, 21 OP_DISABLED,
20 OP_KICK, 22 OP_KICK,
21 OP_BAN, 23 OP_BAN,
22 OP_TOOBIG 24 OP_TOOBIG
23} 25}
24OperatingMode gOperMode = OP_DISABLED;
25 26
26ArrayList filterlist; 27ArrayList regexlist;
28ArrayList patternlist;
27// Note: Contents of ArrayLists are lost on map change / reload. I should store everything in a database, 29// Note: Contents of ArrayLists are lost on map change / reload. I should store everything in a database,
28// then use the database to populate the list whenever it is cleared 30// then use the database to populate the list whenever it is cleared
29 31
32Database db;
33
34ConVar gcvarOperMode; static const char OPERMODENAME[] = "nameblock_OperatingMode"; const OperatingMode DEFAULTOPERMODE = OP_KICK;
35ConVar gcvarAdmCmdFlag; static const char ADMCMDFLAGNAME[] = "nameblock_AdminCommandFlag"; const int DEFAULTADMCMDFLAG = ADMFLAG_BAN;
36ConVar gcvarRegexCompFlags; static const char REGEXCOMPFLAGSNAME[] = "nameblock_RegexCompilationFlags"; const int DEFAULTREGEXCOMPFLAGS = (PCRE_CASELESS | PCRE_DOTALL | PCRE_EXTENDED | PCRE_UTF8);
37ConVar gcvarAmdImmFlag; static const char ADMINIMMUNITYFLAG[] = "nameblock_AdminImmunityFlag"; const int DEFAULTADMIMMFLAG = ADMFLAG_GENERIC;
38
30public void OnAllPluginsLoaded() { 39public void OnAllPluginsLoaded() {
31 filterlist = new ArrayList(ByteCountToCells(HANDLE_SIZE)); 40 // Initialize and populate datatypes
41 regexlist = new ArrayList(ByteCountToCells(HANDLE_SIZE));
42 patternlist = new ArrayList(ByteCountToCells(PATTERN_MAX_LEN));
43 // db =
44
45 // Register convars
46 char tmp[32]; // Consider this a byte array
47 Format(tmp, sizeof(tmp), "%d", view_as<int>(DEFAULTOPERMODE));
48 gcvarOperMode = CreateConVar(OPERMODENAME, tmp, "Operating mode (disabled, kick, ban, etc.)");
49 gcvarOperMode.IntValue = view_as<int>(DEFAULTOPERMODE);
50
51 Format(tmp, sizeof(tmp), "%d", DEFAULTADMCMDFLAG);
52 gcvarAdmCmdFlag = CreateConVar(ADMCMDFLAGNAME, tmp, "Admin flag to modify pattern list");
53 gcvarAdmCmdFlag.IntValue = DEFAULTADMCMDFLAG;
54
55 Format(tmp, sizeof(tmp), "%d", DEFAULTREGEXCOMPFLAGS);
56 gcvarRegexCompFlags = CreateConVar(REGEXCOMPFLAGSNAME, tmp, "Regular expression compilation flags");
57 gcvarRegexCompFlags.IntValue = DEFAULTREGEXCOMPFLAGS;
58
59 Format(tmp, sizeof(tmp), "%d", DEFAULTADMIMMFLAG);
60 gcvarAmdImmFlag = CreateConVar(ADMINIMMUNITYFLAG, tmp, "Admin immunity flag");
61 gcvarAmdImmFlag.IntValue = DEFAULTADMIMMFLAG;
62
63 // I should make a function for setting up convars
64
65 AutoExecConfig(true, "nameblocker_cvars");
66
67 // Register commands
68 RegAdminCmd("nb_addpattern", registerPattern, gcvarAdmCmdFlag.IntValue, "Add a regex pattern to the filter list");
69 RegAdminCmd("nb_removepattern", deletePattern, gcvarAdmCmdFlag.IntValue, "Remove a regex pattern from the filter list");
70 RegAdminCmd("nb_modifypattern", replacePattern, gcvarAdmCmdFlag.IntValue, "Replace an existing regex pattern with a new pattern");
71 RegAdminCmd("nb_listpatterns", listPatterns, gcvarAdmCmdFlag.IntValue, "List current regex patterns and their indicies");
32} 72}
33 73
34public void OnClientPostAdminCheck(int client) { 74public void OnClientPostAdminCheck(int client) {
@@ -41,27 +81,150 @@ public void OnClientSettingsChanged(int client) {
41 81
42 82
43 83
44void checkName(int client) { 84public Action registerPattern(int client, int args) {
45 if(client <= 0) return; 85 if(args < 2) {
86 ReplyToCommand(client, "Error: missing regex pattern");
87 return Plugin_Handled;
88 }
89
90 char pattern[PATTERN_MAX_LEN];
91 GetCmdArg(1, pattern, sizeof(pattern));
92 // TODO: Error handling
93
94 if(insertPattern(pattern, regexlist.Length - 1)) {
95 ReplyToCommand(client, "Error: could not register pattern");
96 return Plugin_Handled
97 }
98
99 return Plugin_Handled;
100}
101
102public Action deletePattern(int client, int args) {
103 if(args < 2) {
104 ReplyToCommand(client, "Error: no pattern index given");
105 return Plugin_Handled;
106 }
107
108 int index
109 if(!GetCmdArgIntEx(1, index)) {
110 ReplyToCommand(client, "Error: index argument not numerical");
111 return Plugin_Handled;
112 }
113
114 if(removePattern(index)) {
115 ReplyToCommand(client, "Error: could not remove pattern");
116 return Plugin_Handled;
117 }
118
119 return Plugin_Handled;
120}
121
122public Action replacePattern(int client, int args) {
123 if(args < 3) {
124 ReplyToCommand(client, "Error: missing index, replacement pattern, or both");
125 return Plugin_Handled;
126 }
127
128 int index
129 if(!GetCmdArgIntEx(1, index)) {
130 ReplyToCommand(client, "Error: index argument not numerical");
131 return Plugin_Handled;
132 }
133
134 char pattern[PATTERN_MAX_LEN];
135 GetCmdArg(2, pattern, sizeof(pattern));
136 // TODO: Error handling
137
138 if(removePattern(index)) {
139 ReplyToCommand(client, "Error: could not remove pattern");
140 return Plugin_Handled;
141 }
142 if(insertPattern(pattern, regexlist.Length - 1)) {
143 ReplyToCommand(client, "Error: could not register pattern");
144 return Plugin_Handled
145 }
146 // Preferably this would be atomic as to not lose a pattern, but that's something I can do later
147
148 // I know I can make the database change "atomic" via the use of a transaction, but handling that would necessitate a different
149 // function
150
151 return Plugin_Handled;
152}
153
154public Action listPatterns(int client, int args) {
155 for(int i = 0; i < patternlist.Length; i++) {
156 ReplyToCommand(client, "[%d] %s", i, patternlist.Get(i));
157 }
158
159 return Plugin_Handled;
160}
161
162
163
164int insertPattern(char pattern[PATTERN_MAX_LEN], int index) {
165 if(IsNullString(pattern) || index < 0 || index >= regexlist.Length) return -1;
166
167 static char errstr[512]; static RegexError reerr;
168 Regex res = CompileRegex(pattern, gcvarRegexCompFlags.IntValue, errstr, sizeof(errstr), reerr);
169 if(res == null) {
170 LogError("Error: Could not compile regex pattern \"%s\": %s (%d)", pattern, errstr, reerr);
171 return -1;
172 }
173
174 if(index != (regexlist.Length - 1)) {
175 regexlist.ShiftUp(index);
176 patternlist.ShiftUp(index);
177 regexlist.Set(index, res);
178 patternlist.Set(index, pattern);
179 } else {
180 regexlist.Push(res);
181 patternlist.Push(pattern);
182 }
183
184 // TODO: This should also insert the pattern into the database
185
186 return 0;
187}
188
189int removePattern(int index) {
190 if(index < 0 || index >= regexlist.Length) return -1;
191
192 regexlist.Erase(index);
193 patternlist.Erase(index);
194
195 // TODO: This should also remove the pattern from the database
196
197 return 0;
198}
199
200
201
202int checkName(int client) {
203 if(client <= 0) return -1;
204 if(gcvarOperMode.IntValue == view_as<int>(OP_DISABLED)) return -1;
205 if(CheckCommandAccess(client, "", gcvarAmdImmFlag.IntValue, true)) return -1;
46 206
47 char name[64 + 1]; 207 char name[64 + 1];
48 if(getName(client, name, sizeof(name)) <= 0) 208 if(getName(client, name, sizeof(name)) <= 0) {
49 return; // TODO: better error checking 209 LogError("Tried to get a client's name for a name check, but could not")
210 return -1;
211 }
50 212
51 RegexError reerr; 213 RegexError reerr;
52 for(int i = 0, m = 0; i < filterlist.Length; i++) { 214 for(int i = 0, m = 0; i < regexlist.Length; i++) {
53 m = MatchRegex(filterlist.Get(i), name, reerr); 215 m = MatchRegex(regexlist.Get(i), name, reerr);
54 216
55 if(m == 0) continue;
56 if(m < 0) { 217 if(m < 0) {
57 handleFailedRegex(client, reerr); 218 handleFailedRegex(client, reerr);
58 return; 219 return -1;
59 } 220 }
221 if(m == 0) continue;
60 222
61 handleNameHit(client); 223 handleNameHit(client);
224 break;
62 } 225 }
63 226
64 return; 227 return 0;
65} 228}
66 229
67int getName(int client, char[] buf, int buflen) { 230int getName(int client, char[] buf, int buflen) {
@@ -74,11 +237,11 @@ int getName(int client, char[] buf, int buflen) {
74int handleNameHit(int client) { 237int handleNameHit(int client) {
75 if(client <= 0) return -1; 238 if(client <= 0) return -1;
76 239
77 switch(gOperMode) { 240 switch(gcvarOperMode.IntValue) {
78 case OP_DISABLED: {return 0;} 241 case OP_DISABLED: {return 0;}
79 case OP_KICK: { 242 case OP_KICK: {
80 KickClient(client, "Failed Name Check"); 243 KickClient(client, "Failed name check");
81 // Log kick 244 LogAction(0, client, "Kicked %L for failing a name check", client);
82 return 0; 245 return 0;
83 } 246 }
84 case OP_BAN: { 247 case OP_BAN: {
@@ -87,7 +250,7 @@ int handleNameHit(int client) {
87 // Log ban 250 // Log ban
88 } 251 }
89 default: { 252 default: {
90 LogError("Operating mode in an invalid state"); 253 LogError("%L failed a name check, but the operating mode in an invalid state", client);
91 return -1; 254 return -1;
92 } 255 }
93 } 256 }
@@ -104,6 +267,8 @@ int handleFailedRegex(int client, RegexError reerr) {
104 return 0; 267 return 0;
105} 268}
106 269
270
271
107// Note: May or may not be particularly descriptive for any given error 272// Note: May or may not be particularly descriptive for any given error
108int RegexStrError(RegexError err, char[] buf, int buflen) { 273int RegexStrError(RegexError err, char[] buf, int buflen) {
109 if(IsNullString(buf)) return -1; 274 if(IsNullString(buf)) return -1;