From 241d430ad2eaefa63a675e1418dff8ad1a01b3f0 Mon Sep 17 00:00:00 2001 From: nwrl Date: Sat, 9 Aug 2025 19:11:13 -0500 Subject: Flesh out sql commands --- nameblocker.sp | 264 +++++++++++++++++++++++---------------------------------- 1 file changed, 104 insertions(+), 160 deletions(-) (limited to 'nameblocker.sp') diff --git a/nameblocker.sp b/nameblocker.sp index 8e6ab33..9611efa 100644 --- a/nameblocker.sp +++ b/nameblocker.sp @@ -52,6 +52,8 @@ public Plugin myinfo = { // 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) +#define STEAMID64LENGTH 17 + #define PATTERN_MAX_LEN (512 + 1) /* 512 chars + null terminator */ #define DATABASE_FAIL_MSG "Could not populate regex & pattern lists from database" @@ -65,11 +67,18 @@ enum OperatingMode { ArrayList regexlist; ArrayList patternlist; -Database db; static const char DBTABLENAME[] = "163687013_SMNameBlocker"; -DBStatement dbInsert; static const char DBINSERTSTATEMENT[] = "INSERT OR IGNORE INTO ? (regexstr, steamid64) VALUES (?, ?);"; -DBStatement dbDelete; static const char DBDELETESTATEMENT[] = "DELETE FROM ? WHERE regexstr=?;"; -DBStatement dbReplace; static const char DBREPLACESTATEMENT[] = "UPDATE OR IGNORE ? SET regexstr=?, steamid64=? WHERE regexstr=?;"; -DBStatement dbPopulate; static const char DBPOPULATESTATEMENT[] = "SELECT regexstr FROM ?"; +#define DBTABLENAME "163687013_SMNameBlocker" +#define DBCREATETABLE "CREATE TABLE IF NOT EXISTS " ... DBTABLENAME ... " (id INTEGER NOT NULL, regexstr TEXT NOT NULL ON CONFLICT IGNORE, steamid64 TEXT NOT NULL ON CONFLICT IGNORE, dateof TEXT DEFAULT CURRENT_DATE, timeof TEXT DEFAULT CURRENT_TIME, PRIMARY KEY (id), UNIQUE (regexstr));" +#define DBINSERTSTATEMENT "INSERT OR IGNORE INTO " ... DBTABLENAME ... " (regexstr, steamid64) VALUES (?, ?);" +#define DBDELETESTATEMENT "DELETE FROM " ... DBTABLENAME ... " WHERE regexstr=?;" +#define DBREPLACESTATEMENT "UPDATE OR IGNORE " ... DBTABLENAME ... " SET regexstr=?, steamid64=? WHERE regexstr=?;" +#define DBPOPULATESTATEMENT "SELECT regexstr FROM " ... DBTABLENAME + +Database db; +DBStatement dbInsert; +DBStatement dbDelete; +DBStatement dbReplace; +DBStatement dbPopulate; ConVar gcvarOperMode; static const char OPERMODENAME[] = "nameblock_OperatingMode"; const OperatingMode DEFAULTOPERMODE = OP_KICK; @@ -125,85 +134,66 @@ void xRegisterIntConVar(ConVar& cv, int defaultVal, const char[] name, const cha int initPrepStatements() { char sqlerr[256 + 1]; - + int err = 0; + SQL_LockDatabase(db); - if((dbInsert = SQL_PrepareQuery(db, DBINSERTSTATEMENT, sqlerr, sizeof(sqlerr))) == null) { - SQL_UnlockDatabase(db); - logAndFail("Could not prepare insert statement: %s", sqlerr); - } - if((dbDelete = SQL_PrepareQuery(db, DBDELETESTATEMENT, sqlerr, sizeof(sqlerr))) == null) { - SQL_UnlockDatabase(db); - logAndFail("Could not prepare delete statement: %s", sqlerr); - } - if((dbReplace = SQL_PrepareQuery(db, DBREPLACESTATEMENT, sqlerr, sizeof(sqlerr))) == null) { - SQL_UnlockDatabase(db); - logAndFail("Could not prepare replace statement: %s", sqlerr); - } - if((dbPopulate = SQL_PrepareQuery(db, DBPOPULATESTATEMENT, sqlerr, sizeof(sqlerr))) == null) { - SQL_UnlockDatabase(db); - logAndFail("Could not prepare populate statement: %s", sqlerr); - } + if((dbInsert = SQL_PrepareQuery(db, DBINSERTSTATEMENT, sqlerr, sizeof(sqlerr))) == null && !err) + err = 1; - // This might not work / I might have to use Format() instead of binding. We will see - if(SQL_BindParamString(dbInsert, 0, DBTABLENAME, true)) { - SQL_GetError(dbInsert, sqlerr, sizeof(sqlerr)); - SQL_UnlockDatabase(db); - logAndFail("Could not bind tablename to insert statement: %s", sqlerr); - } - if(SQL_BindParamString(dbDelete, 0, DBTABLENAME, true)) { - SQL_GetError(dbDelete, sqlerr, sizeof(sqlerr)); - SQL_UnlockDatabase(db); - logAndFail("Could not bind tablename to delete statement: %s", sqlerr); - } - if( SQL_BindParamString(dbReplace, 0, DBTABLENAME, true)) { - SQL_GetError(dbReplace, sqlerr, sizeof(sqlerr)); - SQL_UnlockDatabase(db); - logAndFail("Could not bind tablename to replace statement: %s", sqlerr); - } - if(SQL_BindParamString(dbPopulate, 0, DBTABLENAME, true)) { - SQL_GetError(dbPopulate, sqlerr, sizeof(sqlerr)); - SQL_UnlockDatabase(db); - logAndFail("Could not bind tablename to populate statement: %s", sqlerr); - } + if((dbDelete = SQL_PrepareQuery(db, DBDELETESTATEMENT, sqlerr, sizeof(sqlerr))) == null && !err) + err = 2; - // If I knew how to do macros this would be much nicer + if((dbReplace = SQL_PrepareQuery(db, DBREPLACESTATEMENT, sqlerr, sizeof(sqlerr))) == null && !err) + err = 3; + + if((dbPopulate = SQL_PrepareQuery(db, DBPOPULATESTATEMENT, sqlerr, sizeof(sqlerr))) == null && !err) + err = 4; SQL_UnlockDatabase(db); + switch(err) { + case 1: {logAndFail("Could not prepare insert statement: %s", sqlerr);} + case 2: {logAndFail("Could not prepare delete statement: %s", sqlerr);} + case 3: {logAndFail("Could not prepare replace statement: %s", sqlerr);} + case 4: {logAndFail("Could not prepare populate statement: %s", sqlerr);} + } + return 0; } int loadFromDatabase() { // Initialize and populate datatypes - regexlist = new ArrayList(ByteCountToCells(HANDLE_SIZE)); + regexlist = new ArrayList(ByteCountToCells(HANDLE_SIZE)); if(regexlist == null) logAndFail("Could not initialize regexlist ArrayList"); patternlist = new ArrayList(ByteCountToCells(PATTERN_MAX_LEN)); if(patternlist == null) logAndFail("Could not initialize patternlist ArrayList"); + // Get database handle char sqlerr[256 + 1]; db = SQLite_UseDatabase("sourcemod-local", sqlerr, sizeof(sqlerr)); if(db == null) logAndFail("Could not connect to sql database: %s", sqlerr); // Initialize table if it doesn't exist - // I could make this a prepared statement, but I don't believe it's entirely necessary - char sqlcbuf[256 + 1]; - Format(sqlcbuf, sizeof(sqlcbuf), "CREATE TABLE IF NOT EXISTS \"%s\" (id INTEGER NOT NULL, regexstr TEXT NOT NULL ON CONFLICT IGNORE, steamid64 TEXT NOT NULL ON CONFLICT IGNORE, dateof TEXT DEFAULT CURRENT_DATE, timeof TEXT DEFAULT CURRENT_TIME, PRIMARY KEY (id), UNIQUE (regexstr));", DBTABLENAME); - + int err = 0; SQL_LockDatabase(db); - if(!SQL_FastQuery(db, sqlcbuf)) { + if(!SQL_FastQuery(db, DBCREATETABLE)) { SQL_GetError(db, sqlerr, sizeof(sqlerr)); - SQL_UnlockDatabase(db); - logAndFail("Could not initialize nameblocker table: %s", sqlerr); + err = 1; } // select patterns from nameblock table/database - if(SQL_Execute(dbPopulate)) { + if(SQL_Execute(dbPopulate) && !err) { SQL_GetError(dbPopulate, sqlerr, sizeof(sqlerr)); - SQL_UnlockDatabase(db); - logAndFail("Population query failed"); + err = 2; } + SQL_UnlockDatabase(db); + switch(err) { + case 1: {logAndFail("Could not initialize nameblocker table: %s", sqlerr);} + case 2: {logAndFail("Population query failed: %s", sqlerr);} + } + // compile each pattern & insert Regex cur; char reerr[256]; RegexError reenum; @@ -231,10 +221,10 @@ public void OnAllPluginsLoaded() { loadFromDatabase(); // Register convars - xRegisterIntConVar(gcvarOperMode, view_as(DEFAULTOPERMODE), OPERMODENAME, "Operating mode (disabled, kick, ban, etc.)"); - xRegisterIntConVar(gcvarAdmCmdFlag, DEFAULTADMCMDFLAG, ADMCMDFLAGNAME, "Admin flag to modify pattern list"); - xRegisterIntConVar(gcvarRegexCompFlags, DEFAULTREGEXCOMPFLAGS, REGEXCOMPFLAGSNAME, "Regular expression compilation flags"); - xRegisterIntConVar(gcvarAmdImmFlag, DEFAULTADMIMMFLAG, ADMINIMMUNITYFLAGNAME, "Admin immunity flag"); + xRegisterIntConVar(gcvarOperMode, DEFAULTOPERMODE, OPERMODENAME, "Operating mode (disabled, kick, ban, etc.)"); + xRegisterIntConVar(gcvarAdmCmdFlag, DEFAULTADMCMDFLAG, ADMCMDFLAGNAME, "Admin flag to modify pattern list"); + xRegisterIntConVar(gcvarRegexCompFlags, DEFAULTREGEXCOMPFLAGS, REGEXCOMPFLAGSNAME, "Regular expression compilation flags"); + xRegisterIntConVar(gcvarAmdImmFlag, DEFAULTADMIMMFLAG, ADMINIMMUNITYFLAGNAME, "Admin immunity flag"); AutoExecConfig(true, "nameblocker_cvars"); @@ -341,49 +331,74 @@ enum MOD_MODE { MM_TOOBIG }; -int modPattern(MOD_MODE mode, int index, char[] pattern="", int patternlen=0) { - if(index < 0 || index > regexlist.Length) return -1; +int __modPattern__insert(int index, char[] pattern, int patternlen, int client) { + if(IsNullString(pattern) || patternlen < 0 || patternlen > PATTERN_MAX_LEN || client < 0) {return -1;} - switch(mode) { - case MM_INSERT: { - if(IsNullString(pattern) || patternlen < 0 || patternlen > PATTERN_MAX_LEN) {return -1;} + char steamid64[STEAMID64LENGTH + 1]; + if(GetClientAuthId(client, AuthId_SteamID64, steamid64, sizeof(steamid64))) {} // TODO: Error handling - // Add pattern to database - SQL_LockDatabase(db); - SQL_UnlockDatabase(db); + SQL_LockDatabase(db); + SQL_BindParamString(dbInsert, 0, pattern, false); + SQL_BindParamString(dbInsert, 1, steamid64, false); + if(!SQL_Execute(dbInsert)) {} // TODO: Error handling + SQL_UnlockDatabase(db); - aModPattern(mode, index, pattern); - } + if(aModPattern(MM_INSERT, index, pattern)) {} // TODO: Error handling - case MM_DELETE: { - // Detele from database - SQL_LockDatabase(db); - SQL_UnlockDatabase(db); + return 0; +} +int __modPattern__delete(int index) { + char pattern[PATTERN_MAX_LEN]; + patternlist.GetString(index, pattern, sizeof(pattern), ByteCountToCells(PATTERN_MAX_LEN)); - aModPattern(mode, index); - } + SQL_LockDatabase(db); + SQL_BindParamString(dbDelete, 0, pattern, false); + if(SQL_Execute(dbDelete)) {} // TODO: Error handling + SQL_UnlockDatabase(db); - case MM_REPLACE: { - LogError("Replace mode not implemented yet"); - return -1; + if(aModPattern(MM_DELETE, index)) {} // TODO: Error handling + + return 0; +} +int __modPattern__replace(int index, char[] pattern, int patternlen, int client) { + if(IsNullString(pattern) || patternlen < 0 || patternlen > PATTERN_MAX_LEN || client < 0) {return -1;} - // Replace in database - // SQL_LockDatabase(db); - // SQL_UnlockDatabase(db); + char oldpattern[PATTERN_MAX_LEN]; + patternlist.GetString(index, oldpattern, sizeof(oldpattern), ByteCountToCells(PATTERN_MAX_LEN)); - // aModPattern(mode, index, pattern); - } + char steamid64[STEAMID64LENGTH]; + if(GetClientAuthId(client, AuthId_SteamID64, steamid64, sizeof(steamid64))) {} // TODO: Error handling + + SQL_LockDatabase(db); + SQL_BindParamString(dbReplace, 0, pattern, false); + SQL_BindParamString(dbReplace, 1, steamid64, false); + SQL_BindParamString(dbReplace, 2, oldpattern, false); + + if(SQL_Execute(dbReplace)) {} // TODO: Error handling + SQL_UnlockDatabase(db); + + if(aModPattern(MM_REPLACE, index, pattern)) {} // TODO: Error handling + + return 0; +} + +int modPattern(MOD_MODE mode, int index, char[] pattern="", int patternlen=-1, int client=-1) { + if(index < 0 || index > regexlist.Length) return -1; + switch(mode) { + case MM_INSERT: {return __modPattern__insert(index, pattern, patternlen, client);} + case MM_DELETE: {return __modPattern__delete(index);} + case MM_REPLACE: {return __modPattern__replace(index, pattern, patternlen, client);} default: { LogError("Given invalid DBMOD_MODE"); return -1; } } - - return 0; } int aModPattern(MOD_MODE mode, int index, char[] pattern="", int patternlen=0) { + LogError("aModPattern not implemented"); return -1; + if(mode <= MM_UNDEF || mode >= MM_TOOBIG || index < 0 || index > patternlist.Length) return -1; if(mode > MM_DELETE) { @@ -448,7 +463,7 @@ int checkName(int client) { m = MatchRegex(regexlist.Get(i), name, reerr); if(m < 0) { - handleFailedRegex(client, reerr); + handleFailedRegex(client); return -1; } if(m == 0) continue; @@ -481,87 +496,16 @@ int handleNameHit(int client) { // BanClient() // TODO: Interop with other ban systems // Log ban + return 0; } default: { LogError("%L failed a name check, but the operating mode in an invalid state", client); 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) { - 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); +int handleFailedRegex(int client) { + LogError("Ran into regex error when trying to check user %L's name", client); 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; - - char tmp[64]; - switch(err) { - case REGEX_ERROR_NONE: {tmp = "No error";} - - case REGEX_ERROR_ASSERT: {tmp = "Internal error";} - case REGEX_ERROR_BADBR: {tmp = "Invalid repeat counts in {}";} - case REGEX_ERROR_BADPAT: {tmp = "Pattern error";} - case REGEX_ERROR_BADRPT: {tmp = "? * + invalid";} - case REGEX_ERROR_EBRACE: {tmp = "Unbalanced {}";} - case REGEX_ERROR_EBRACK: {tmp = "Unbalanced []";} - case REGEX_ERROR_ECOLLATE: {tmp = "Collation error - not relevant";} - case REGEX_ERROR_ECTYPE: {tmp = "Bad class";} - case REGEX_ERROR_EESCAPE: {tmp = "Bad escape sequence";} - case REGEX_ERROR_EMPTY: {tmp = "Empty expression";} - case REGEX_ERROR_EPAREN: {tmp = "Unbalanced ()";} - case REGEX_ERROR_ERANGE: {tmp = "Bad range inside []";} - case REGEX_ERROR_ESIZE: {tmp = "Expression too big";} - case REGEX_ERROR_ESPACE: {tmp = "Failed to get memory";} - case REGEX_ERROR_ESUBREG: {tmp = "Bad back reference";} - case REGEX_ERROR_INVARG: {tmp = "Bad argument";} - - case REGEX_ERROR_NOMATCH: {tmp = "No match was found";} - case REGEX_ERROR_NULL: {tmp = "Null";} - case REGEX_ERROR_BADOPTION: {tmp = "Bad Option";} - case REGEX_ERROR_BADMAGIC: {tmp = "Bad Magic";} - case REGEX_ERROR_UNKNOWN_OPCODE: {tmp = "Unknown OpCode";} - case REGEX_ERROR_NOMEMORY: {tmp = "No Memory";} - case REGEX_ERROR_NOSUBSTRING: {tmp = "No substring";} - case REGEX_ERROR_MATCHLIMIT: {tmp = "Match limit";} - case REGEX_ERROR_CALLOUT: {tmp = "Callout";} // Never used by PCRE itself - case REGEX_ERROR_BADUTF8: {tmp = "Bad UTF8";} - case REGEX_ERROR_BADUTF8_OFFSET: {tmp = "Bad UTF8 offset";} - case REGEX_ERROR_PARTIAL: {tmp = "Partial";} - case REGEX_ERROR_BADPARTIAL: {tmp = "Bad Partial";} - case REGEX_ERROR_INTERNAL: {tmp = "Internal error";} - case REGEX_ERROR_BADCOUNT: {tmp = "Bad count";} - case REGEX_ERROR_DFA_UITEM: {tmp = "DFA UItem";} - case REGEX_ERROR_DFA_UCOND: {tmp = "DFA UCOND";} - case REGEX_ERROR_DFA_UMLIMIT: {tmp = "DFA UMLIMIT";} - case REGEX_ERROR_DFA_WSSIZE: {tmp = "DFA WSSIZE";} - case REGEX_ERROR_DFA_RECURSE: {tmp = "DFA recurse";} - case REGEX_ERROR_RECURSIONLIMIT: {tmp = "Recursion Limit";} - case REGEX_ERROR_NULLWSLIMIT: {tmp = "NULL WSLIMIT";} /* No longer actually used */ - case REGEX_ERROR_BADNEWLINE: {tmp = "Bad newline";} - case REGEX_ERROR_BADOFFSET: {tmp = "Bad offset";} - case REGEX_ERROR_SHORTUTF8: {tmp = "Short UFT8";} - case REGEX_ERROR_RECURSELOOP: {tmp = "Recurse loop";} - case REGEX_ERROR_JIT_STACKLIMIT: {tmp = "JIT Stacklimit";} - case REGEX_ERROR_BADMODE: {tmp = "Bad mode";} - case REGEX_ERROR_BADENDIANNESS: {tmp = "Bad endianness";} - case REGEX_ERROR_DFA_BADRESTART: {tmp = "DFA Bad Restart";} - case REGEX_ERROR_JIT_BADOPTION: {tmp = "JIT bad option";} - case REGEX_ERROR_BADLENGTH: {tmp = "Bad length";} - - default: {tmp = "Unknown Error";} - } - - return strcopy(buf, buflen, tmp); } \ No newline at end of file -- cgit v1.2.3