diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 4080c325..1a60f756 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -88,6 +88,8 @@ var/aurorawikiurl var/githuburl + var/whitelists_on_sql = 0 //Changes how whitelists are handled. SQL connection required to run this! + //Alert level description var/alert_desc_green = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced." var/alert_desc_blue_upto = "The station has received reliable information about possible hostile activity on the station. Security staff may have weapons visible, random searches are permitted." @@ -528,6 +530,9 @@ if("topic_safe_address") topic_safe_address = value + if ("whitelists_on_sql") + config.whitelists_on_sql = 1 + else log_misc("Unknown setting in configuration: '[name]'") diff --git a/code/defines/procs/dbcore.dm b/code/defines/procs/dbcore.dm index ddc7191e..af387497 100644 --- a/code/defines/procs/dbcore.dm +++ b/code/defines/procs/dbcore.dm @@ -48,45 +48,45 @@ DBConnection var/server = "" var/port = 3306 -DBConnection/New(dbi_handler,username,password_handler,cursor_handler) - src.dbi = dbi_handler - src.user = username - src.password = password_handler - src.default_cursor = cursor_handler +DBConnection/New(dbi_handler, username, password_handler, cursor_handler) + dbi = dbi_handler + user = username + password = password_handler + default_cursor = cursor_handler _db_con = _dm_db_new_con() -DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) - if(!sqllogging) +DBConnection/proc/Connect(dbi_handler = dbi, user_handler = user, password_handler = password, cursor_handler) + if (!sqllogging) return 0 - if(!src) return 0 - cursor_handler = src.default_cursor - if(!cursor_handler) cursor_handler = Default_Cursor - return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) + if (!src) + return 0 + cursor_handler = default_cursor + if (!cursor_handler) + cursor_handler = Default_Cursor + return _dm_db_connect(_db_con,dbi_handler, user_handler, password_handler, cursor_handler, null) -DBConnection/proc/Disconnect() return _dm_db_close(_db_con) +DBConnection/proc/Disconnect() + return _dm_db_close(_db_con) DBConnection/proc/IsConnected() - if(!sqllogging) return 0 + if(!sqllogging) + return 0 var/success = _dm_db_is_connected(_db_con) return success -DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str) +DBConnection/proc/Quote(str) + return _dm_db_quote(_db_con,str) + +DBConnection/proc/ErrorMsg() + return _dm_db_error_msg(_db_con) -DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) DBConnection/proc/SelectDB(database_name,dbi) if(IsConnected()) Disconnect() //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) - return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) -DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) - - -DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) - if(sql_query) src.sql = sql_query - if(connection_handler) src.db_connection = connection_handler - if(cursor_handler) src.default_cursor = cursor_handler - _db_query = _dm_db_new_query() - return ..() + return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]", user, password) +DBConnection/proc/NewQuery(sql_query, cursor_handler = default_cursor) + return new/DBQuery(sql_query, src, cursor_handler) DBQuery var/sql // The sql query being executed. @@ -98,34 +98,53 @@ DBQuery var/DBConnection/db_connection var/_db_query -DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler +DBQuery/New(var/sql_query, var/DBConnection/connection_handler, var/cursor_handler) + if (sql_query) + sql = sql_query + if (connection_handler) + db_connection = connection_handler + if (cursor_handler) + default_cursor = cursor_handler + _db_query = _dm_db_new_query() + return ..() -DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) +DBQuery/proc/Connect(DBConnection/connection_handler) + db_connection = connection_handler + +DBQuery/proc/Execute(var/list/argumentList = null, var/passNotFound = 0, sql_query = sql, cursor_handler = default_cursor) Close() - return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) -DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) + if (argumentList) + parseArguments(argumentList, passNotFound) -DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) + return _dm_db_execute(_db_query, sql_query, db_connection._db_con, cursor_handler, null) -DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) +DBQuery/proc/NextRow() + return _dm_db_next_row(_db_query,item,conversions) -DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) +DBQuery/proc/RowsAffected() + return _dm_db_rows_affected(_db_query) + +DBQuery/proc/RowCount() + return _dm_db_row_count(_db_query) + +DBQuery/proc/ErrorMsg() + return _dm_db_error_msg(_db_query) DBQuery/proc/Columns() - if(!columns) + if (!columns) columns = _dm_db_columns(_db_query,/DBColumn) return columns DBQuery/proc/GetRowData() var/list/columns = Columns() var/list/results - if(columns.len) + if (columns.len) results = list() - for(var/C in columns) - results+=C + for (var/C in columns) + results += C var/DBColumn/cur_col = columns[C] - results[C] = src.item[(cur_col.position+1)] + results[C] = item[(cur_col.position+1)] return results DBQuery/proc/Close() @@ -138,11 +157,49 @@ DBQuery/proc/Quote(str) return db_connection.Quote(str) DBQuery/proc/SetConversion(column,conversion) - if(istext(column)) column = columns.Find(column) - if(!conversions) conversions = new/list(column) - else if(conversions.len < column) conversions.len = column + if (istext(column)) + column = columns.Find(column) + if (!conversions) + conversions = new/list(column) + else if (conversions.len < column) + conversions.len = column conversions[column] = conversion +/* Works similarly to the PDO object's Execute() method in PHP. +* Insert a list of keys/values, it searches the SQL syntax for the keys, +* and replaces them with sanitized versions of the values. +* Can be called independently, or through dbcon.Execute(), where the list would be the first argument. +* passNotFound controls whether or not is passes keys not found in the SQL query. +* Keys are /case-sensitive/, be careful! +* Returns FALSE upon failure, TRUE otherwise, so it can be used in checks and so on. +* - Skull132 +*/ +DBQuery/proc/parseArguments(var/list/argumentList, var/passNotFound = 0) + if (!sql || !argumentList || !argumentList.len) + return 0 + + for (var/placeholder in argumentList) + if (!findtextEx(sql, placeholder)) + if (passNotFound) + continue + else + return 0 + + var/argument = argumentList[placeholder] + + if (isnull(argument)) + argument = "NULL" + else if (istext(argument)) + argument = dbcon.Quote(argument) + else if (isnum(argument)) + argument = "'[argument]'" + else + return 0 + + sql = replacetextEx(sql, placeholder, argument) + + return 1 + DBColumn var/name @@ -153,32 +210,45 @@ DBColumn var/length var/max_length -DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) - src.name = name_handler - src.table = table_handler - src.position = position_handler - src.sql_type = type_handler - src.flags = flag_handler - src.length = length_handler - src.max_length = max_length_handler +DBColumn/New(name_handler, table_handler, position_handler, type_handler, flag_handler, length_handler, max_length_handler) + name = name_handler + table = table_handler + position = position_handler + sql_type = type_handler + flags = flag_handler + length = length_handler + max_length = max_length_handler return ..() -DBColumn/proc/SqlTypeName(type_handler=src.sql_type) - switch(type_handler) - if(TINYINT) return "TINYINT" - if(SMALLINT) return "SMALLINT" - if(MEDIUMINT) return "MEDIUMINT" - if(INTEGER) return "INTEGER" - if(BIGINT) return "BIGINT" - if(FLOAT) return "FLOAT" - if(DOUBLE) return "DOUBLE" - if(DATE) return "DATE" - if(DATETIME) return "DATETIME" - if(TIMESTAMP) return "TIMESTAMP" - if(TIME) return "TIME" - if(STRING) return "STRING" - if(BLOB) return "BLOB" +DBColumn/proc/SqlTypeName(type_handler = sql_type) + switch (type_handler) + if (TINYINT) + return "TINYINT" + if (SMALLINT) + return "SMALLINT" + if (MEDIUMINT) + return "MEDIUMINT" + if (INTEGER) + return "INTEGER" + if (BIGINT) + return "BIGINT" + if (FLOAT) + return "FLOAT" + if (DOUBLE) + return "DOUBLE" + if (DATE) + return "DATE" + if (DATETIME) + return "DATETIME" + if (TIMESTAMP) + return "TIMESTAMP" + if (TIME) + return "TIME" + if (STRING) + return "STRING" + if (BLOB) + return "BLOB" #undef Default_Cursor diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm index f9bb469f..599517ce 100644 --- a/code/game/jobs/whitelist.dm +++ b/code/game/jobs/whitelist.dm @@ -3,18 +3,36 @@ var/list/whitelist = list() /hook/startup/proc/loadWhitelist() - if(config.usewhitelist) + if (config.usewhitelist) load_whitelist() return 1 /proc/load_whitelist() - whitelist = file2list(WHITELISTFILE) - if(!whitelist.len) whitelist = null + if (config.whitelists_on_sql) + establish_db_connection() + + if (!dbcon.IsConnected()) + //Continue with the old code if it fails. Stop and return if it succeeds. + log_misc("Database connection failed. Reverting to legacy system.") + config.whitelists_on_sql = 0 + else + return + + whitelist = file2list(WHITELISTFILE) + if (!whitelist.len) + whitelist = null + +/proc/check_whitelist(mob/M) + if (config.whitelists_on_sql) + var/head_of_staff_whitelist = 1 + if (M.client && M.client.whitelist_status) + return (M.client.whitelist_status & head_of_staff_whitelist) -/proc/check_whitelist(mob/M /*, var/rank*/) - if(!whitelist) return 0 - return ("[M.ckey]" in whitelist) + else + if (!whitelist) + return 0 + return ("[M.ckey]" in whitelist) /var/list/alien_whitelist = list() @@ -24,6 +42,22 @@ var/list/whitelist = list() return 1 /proc/load_alienwhitelist() + if (config.whitelists_on_sql) + establish_db_connection() + + if (!dbcon.IsConnected()) + log_misc("Database connection failed. Reverting to legacy system.") + config.whitelists_on_sql = 0 + else + var/DBQuery/query = dbcon.NewQuery("SELECT status_name, flag FROM ss13_whitelist_statuses") + query.Execute() + + for (query.NextRow()) + if (query.item[1] in whitelisted_species) + whitelisted_species[query.item[1]] = text2num(query.item[2]) + + return + var/text = file2text("config/alienwhitelist.txt") if (!text) log_misc("Failed to load config/alienwhitelist.txt") @@ -32,19 +66,26 @@ var/list/whitelist = list() //todo: admin aliens /proc/is_alien_whitelisted(mob/M, var/species) - if(!config.usealienwhitelist) + if (!config.usealienwhitelist) return 1 - if(species == "human" || species == "Human") - return 1 -// if(check_rights(R_ADMIN, 0)) -// return 1 - if(!alien_whitelist) + + if (!M || !species) return 0 - if(M && species) + + if (species == "human" || species == "Human") + return 1 + + if (config.whitelists_on_sql) + if (M.client && M.client.whitelist_status) + return (M.client.whitelist_status & whitelisted_species[species]) + + else + if (!alien_whitelist) + return 0 for (var/s in alien_whitelist) - if(findtext(s,"[M.ckey] - [species]")) + if (findtext(s, "[M.ckey] - [species]")) return 1 - if(findtext(s,"[M.ckey] - All")) + if (findtext(s, "[M.ckey] - All")) return 1 return 0 diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index cdd394f4..f026c743 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -46,5 +46,6 @@ var/player_age = "Requires database" //So admins know why it isn't working - Used to determine how old the account is - in days. var/related_accounts_ip = "Requires database" //So admins know why it isn't working - Used to determine what other accounts previously logged in from this ip var/related_accounts_cid = "Requires database" //So admins know why it isn't working - Used to determine what other accounts previously logged in from this computer id + var/whitelist_status = 0 //Used to determine what whitelists the player has access to. Bitflag field. preload_rsc = 0 // This is 0 so we can set it to an URL once the player logs in and have them download the resources from a different server. diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index f461943b..135262c9 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -225,15 +225,14 @@ if(!dbcon.IsConnected()) return - var/sql_ckey = sql_sanitize_text(src.ckey) - - var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM ss13_player WHERE ckey = '[sql_ckey]'") - query.Execute() + var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age, whitelist_status FROM ss13_player WHERE ckey = :ckey") + query.Execute(list(":ckey" = ckey)) var/sql_id = 0 player_age = 0 // New players won't have an entry so knowing we have a connection we set this to zero to be updated if their is a record. while(query.NextRow()) sql_id = query.item[1] player_age = text2num(query.item[2]) + whitelist_status = text2num(query.item[3]) break var/DBQuery/query_ip = dbcon.NewQuery("SELECT ckey FROM ss13_player WHERE ip = '[address]'") @@ -261,24 +260,19 @@ if(src.holder) admin_rank = src.holder.rank - var/sql_ip = sql_sanitize_text(src.address) - var/sql_computerid = sql_sanitize_text(src.computer_id) - var/sql_admin_rank = sql_sanitize_text(admin_rank) - - if(sql_id) //Player already identified previously, we need to just update the 'lastseen', 'ip' and 'computer_id' variables - var/DBQuery/query_update = dbcon.NewQuery("UPDATE ss13_player SET lastseen = Now(), ip = '[sql_ip]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]' WHERE id = [sql_id]") - query_update.Execute() + var/DBQuery/query_update = dbcon.NewQuery("UPDATE ss13_player SET lastseen = Now(), ip = :ip, computerid = :computer_id, lastadminrank = :admin_rank WHERE id = :id") + query_update.Execute(list(":ip" = address, ":computer_id" = computer_id, ":admin_rank" = admin_rank, ":id" = sql_id)) else //New player!! Need to insert all the stuff - var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO ss13_player (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[sql_ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]')") - query_insert.Execute() + var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO ss13_player (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, :ckey, Now(), Now(), :ip, :computer_id, :admin_rank)") + query_insert.Execute(list(":ckey" = ckey, ":ip" = address, ":computer_id" = computer_id, ":admin_rank" = admin_rank)) //Logging player access var/serverip = "[world.internet_address]:[world.port]" - var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `ss13_connection_log`(`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[sql_ckey]','[sql_ip]','[sql_computerid]');") - query_accesslog.Execute() + var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `ss13_connection_log`(`id`, `datetime`, `serverip`, `ckey`, `ip`, `computerid`) VALUES(null, Now(), :server_ip, :ckey, :ip, :computer_id);") + query_accesslog.Execute(list(":server_ip" = serverip, ":ckey" = ckey, ":ip" = address, ":computer_id" = computer_id)) #undef TOPIC_SPAM_DELAY