diff options
Diffstat (limited to 'nameblocker.sp')
| -rw-r--r-- | nameblocker.sp | 199 |
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 | |||
| 18 | enum OperatingMode { | 20 | enum 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 | } |
| 24 | OperatingMode gOperMode = OP_DISABLED; | ||
| 25 | 26 | ||
| 26 | ArrayList filterlist; | 27 | ArrayList regexlist; |
| 28 | ArrayList 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 | ||
| 32 | Database db; | ||
| 33 | |||
| 34 | ConVar gcvarOperMode; static const char OPERMODENAME[] = "nameblock_OperatingMode"; const OperatingMode DEFAULTOPERMODE = OP_KICK; | ||
| 35 | ConVar gcvarAdmCmdFlag; static const char ADMCMDFLAGNAME[] = "nameblock_AdminCommandFlag"; const int DEFAULTADMCMDFLAG = ADMFLAG_BAN; | ||
| 36 | ConVar gcvarRegexCompFlags; static const char REGEXCOMPFLAGSNAME[] = "nameblock_RegexCompilationFlags"; const int DEFAULTREGEXCOMPFLAGS = (PCRE_CASELESS | PCRE_DOTALL | PCRE_EXTENDED | PCRE_UTF8); | ||
| 37 | ConVar gcvarAmdImmFlag; static const char ADMINIMMUNITYFLAG[] = "nameblock_AdminImmunityFlag"; const int DEFAULTADMIMMFLAG = ADMFLAG_GENERIC; | ||
| 38 | |||
| 30 | public void OnAllPluginsLoaded() { | 39 | public 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 | ||
| 34 | public void OnClientPostAdminCheck(int client) { | 74 | public void OnClientPostAdminCheck(int client) { |
| @@ -41,27 +81,150 @@ public void OnClientSettingsChanged(int client) { | |||
| 41 | 81 | ||
| 42 | 82 | ||
| 43 | 83 | ||
| 44 | void checkName(int client) { | 84 | public 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 | |||
| 102 | public 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 | |||
| 122 | public 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 | |||
| 154 | public 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 | |||
| 164 | int 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 | |||
| 189 | int 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 | |||
| 202 | int 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 | ||
| 67 | int getName(int client, char[] buf, int buflen) { | 230 | int getName(int client, char[] buf, int buflen) { |
| @@ -74,11 +237,11 @@ int getName(int client, char[] buf, int buflen) { | |||
| 74 | int handleNameHit(int client) { | 237 | int 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 |
| 108 | int RegexStrError(RegexError err, char[] buf, int buflen) { | 273 | int RegexStrError(RegexError err, char[] buf, int buflen) { |
| 109 | if(IsNullString(buf)) return -1; | 274 | if(IsNullString(buf)) return -1; |
