summaryrefslogtreecommitdiff
path: root/nameblocker.sp
blob: 9190dd2bf86c4ebba78e4d1f3c593d9ef8ee2144 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#pragma newdecls required

#include <sourcemod>
#include <regex>

public Plugin myinfo = {
    name            = "SM Name Blocker",
    description     = "A simple plugin to stop people with blacklisted names from joining a server",
    author          = "NW/RL",
    version         = "alpha-0.1",
    url             = "git.dabikers.online/smnameblocker"
};

#define HANDLE_SIZE 32
// 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
// any other methodmap or descendant of Handle (like Regex)

enum OperatingMode {
    OP_DISABLED,
    OP_KICK,
    OP_BAN,
    OP_TOOBIG
}
OperatingMode gOperMode = OP_DISABLED;

ArrayList filterlist;
// Note: Contents of ArrayLists are lost on map change / reload. I should store everything in a database,
// then use the database to populate the list whenever it is cleared

public void OnAllPluginsLoaded() {
    filterlist = new ArrayList(ByteCountToCells(HANDLE_SIZE));
}

public void OnClientPostAdminCheck(int client) {
    checkName(client);
}

public void OnClientSettingsChanged(int client) {
    checkName(client);
}



void checkName(int client) {
    if(client <= 0) return;

    char name[64 + 1];
    if(getName(client, name, sizeof(name)) <= 0)
        return; // TODO: better error checking

    RegexError reerr;
    for(int i = 0, m = 0; i < filterlist.Length; i++) {
        m = MatchRegex(filterlist.Get(i), name, reerr);

        if(m == 0) continue;
        if(m < 0) {
            handleFailedRegex(client, reerr);
            return;
        }

        handleNameHit(client);
    }

    return;
}

int getName(int client, char[] buf, int buflen) {
    if(client <= 0 || buflen <= 0) return -1;
    if(IsNullString(buf)) return -1;

    return Format(buf, buflen, "%N", client);
}

int handleNameHit(int client) {
    if(client <= 0) return -1;

    switch(gOperMode) {
        case OP_DISABLED: {return 0;}
        case OP_KICK: {
            KickClient(client, "Failed Name Check");
            // Log kick
            return 0;
        }
        case OP_BAN: {
            // BanClient()
            // TODO: Interop with other ban systems
            // Log ban
        }
        default: {
            LogError("Operating mode in an invalid state");
            return -1;
        }
    }

    LogError("Broke out of switch statement that shouldn't have happened");
    return -1; // Shouldn't get to this point
}

int handleFailedRegex(int client, RegexError reerr) {
    static char regstr[128];
    RegexStrError(reerr, regstr, sizeof(regstr));

    LogError("Ran into regex error when trying to check user %L's name. Reported regex error: %s", client, regstr);
    return 0;
}

// Note: May or may not be particularly descriptive for any given error
int RegexStrError(RegexError err, char[] buf, int buflen) {
    if(IsNullString(buf)) return -1;

    switch(err) {
        case REGEX_ERROR_NONE: {Format(buf, buflen, "No error");}  /* No error */

        case REGEX_ERROR_ASSERT:    {Format(buf, buflen, "Internal error");}
        case REGEX_ERROR_BADBR:     {Format(buf, buflen, "Invalid repeat counts in {}");}
        case REGEX_ERROR_BADPAT:    {Format(buf, buflen, "Pattern error");}
        case REGEX_ERROR_BADRPT:    {Format(buf, buflen, "? * + invalid");}
        case REGEX_ERROR_EBRACE:    {Format(buf, buflen, "Unbalanced {}");}
        case REGEX_ERROR_EBRACK:    {Format(buf, buflen, "Unbalanced []");}
        case REGEX_ERROR_ECOLLATE:  {Format(buf, buflen, "Collation error - not relevant");}
        case REGEX_ERROR_ECTYPE:    {Format(buf, buflen, "Bad class");}
        case REGEX_ERROR_EESCAPE:   {Format(buf, buflen, "Bad escape sequence");}
        case REGEX_ERROR_EMPTY:     {Format(buf, buflen, "Empty expression");}
        case REGEX_ERROR_EPAREN:    {Format(buf, buflen, "Unbalanced ()");}
        case REGEX_ERROR_ERANGE:    {Format(buf, buflen, "Bad range inside []");}
        case REGEX_ERROR_ESIZE:     {Format(buf, buflen, "Expression too big");}
        case REGEX_ERROR_ESPACE:    {Format(buf, buflen, "Failed to get memory");}
        case REGEX_ERROR_ESUBREG:   {Format(buf, buflen, "Bad back reference");}
        case REGEX_ERROR_INVARG:    {Format(buf, buflen, "Bad argument");}

        case REGEX_ERROR_NOMATCH:           {Format(buf, buflen, "No match was found");}
        case REGEX_ERROR_NULL:              {Format(buf, buflen, "Null");}
        case REGEX_ERROR_BADOPTION:         {Format(buf, buflen, "Bad Option");}
        case REGEX_ERROR_BADMAGIC:          {Format(buf, buflen, "Bad Magic");}
        case REGEX_ERROR_UNKNOWN_OPCODE:    {Format(buf, buflen, "Unknown OpCode");}
        case REGEX_ERROR_NOMEMORY:          {Format(buf, buflen, "No Memory");}
        case REGEX_ERROR_NOSUBSTRING:       {Format(buf, buflen, "No substring");}
        case REGEX_ERROR_MATCHLIMIT:        {Format(buf, buflen, "Match limit");}
        case REGEX_ERROR_CALLOUT:           {Format(buf, buflen, "Callout");} // Never used by PCRE itself
        case REGEX_ERROR_BADUTF8:           {Format(buf, buflen, "Bad UTF8");}
        case REGEX_ERROR_BADUTF8_OFFSET:    {Format(buf, buflen, "Bad UTF8 offset");}
        case REGEX_ERROR_PARTIAL:           {Format(buf, buflen, "Partial");}
        case REGEX_ERROR_BADPARTIAL:        {Format(buf, buflen, "Bad Partial");}
        case REGEX_ERROR_INTERNAL:          {Format(buf, buflen, "Internal error");}
        case REGEX_ERROR_BADCOUNT:          {Format(buf, buflen, "Bad count");}
        case REGEX_ERROR_DFA_UITEM:         {Format(buf, buflen, "DFA UItem");}
        case REGEX_ERROR_DFA_UCOND:         {Format(buf, buflen, "DFA UCOND");}
        case REGEX_ERROR_DFA_UMLIMIT:       {Format(buf, buflen, "DFA UMLIMIT");}
        case REGEX_ERROR_DFA_WSSIZE:        {Format(buf, buflen, "DFA WSSIZE");}
        case REGEX_ERROR_DFA_RECURSE:       {Format(buf, buflen, "DFA recurse");}
        case REGEX_ERROR_RECURSIONLIMIT:    {Format(buf, buflen, "Recursion Limit");}
        case REGEX_ERROR_NULLWSLIMIT:       {Format(buf, buflen, "NULL WSLIMIT");}  /* No longer actually used */
        case REGEX_ERROR_BADNEWLINE:        {Format(buf, buflen, "Bad newline");}
        case REGEX_ERROR_BADOFFSET:         {Format(buf, buflen, "Bad offset");}
        case REGEX_ERROR_SHORTUTF8:         {Format(buf, buflen, "Short UFT8");}
        case REGEX_ERROR_RECURSELOOP:       {Format(buf, buflen, "Recurse loop");}
        case REGEX_ERROR_JIT_STACKLIMIT:    {Format(buf, buflen, "JIT Stacklimit");}
        case REGEX_ERROR_BADMODE:           {Format(buf, buflen, "Bad mode");}
        case REGEX_ERROR_BADENDIANNESS:     {Format(buf, buflen, "Bad endianness");}
        case REGEX_ERROR_DFA_BADRESTART:    {Format(buf, buflen, "DFA Bad Restart");}
        case REGEX_ERROR_JIT_BADOPTION:     {Format(buf, buflen, "JIT bad option");}
        case REGEX_ERROR_BADLENGTH:         {Format(buf, buflen, "Bad length");}

        default: {Format(buf, buflen, "Unknown Error");}
    }

    // Yeah I know this is terrible, and it's entirely because I'm not certain how string assignments work. If this were C I would
    // make a normal char* string and make it point to different text depending on the case

    return 0;
}