diff --git a/SQL/bluemoon_schema.sql b/SQL/bluemoon_schema.sql
index a00aac276bd5..e7433d5855d5 100644
--- a/SQL/bluemoon_schema.sql
+++ b/SQL/bluemoon_schema.sql
@@ -81,47 +81,33 @@ DELIMITER ;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
--
-- Table structure for table `ipintel`
--
+
DROP TABLE IF EXISTS `ipintel`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `ipintel` (
- `ip` int UNSIGNED NOT NULL,
- `date` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL,
- `intel` real NOT NULL DEFAULT '0',
- PRIMARY KEY (`ip`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+CREATE TABLE `ipintel` (
+ `ip` int(10) unsigned NOT NULL,
+ `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `intel` double NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ip`),
+ KEY `idx_ipintel` (`ip`,`intel`,`date`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
--- Table structure for table `vpn_whitelist`
+-- Table structure for table `ipintel_whitelist`
--
-DROP TABLE IF EXISTS `vpn_whitelist`;
+
+DROP TABLE IF EXISTS `ipintel_whitelist`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `vpn_whitelist` (
- `ckey` varchar(32) NOT NULL,
- `reason` text,
- PRIMARY KEY (`ckey`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+CREATE TABLE `ipintel_whitelist` (
+ `ckey` varchar(32) NOT NULL,
+ `admin_ckey` varchar(32) NOT NULL,
+ PRIMARY KEY (`ckey`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `connection_ipintel_log`
---
-DROP TABLE IF EXISTS `connection_ipintel_log`;
-CREATE TABLE `connection_ipintel_log` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
- `datetime` datetime NOT NULL,
- `ckey` varchar(32) NOT NULL,
- `ip` INT UNSIGNED NOT NULL,
- `computerid` varchar(32) NOT NULL,
- `server_id` VARCHAR(50) NULL DEFAULT NULL,
- `result` ENUM('ESTABLISHED','DROPPED - IPINTEL','DROPPED - BANNED','DROPPED - INVALID') NOT NULL DEFAULT 'ESTABLISHED' COLLATE 'utf8mb4_general_ci',
- PRIMARY KEY (`id`),
- KEY `ckey` (`ckey`),
- KEY `ip` (`ip`),
- KEY `computerid` (`computerid`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
diff --git a/code/_BLUEMOONCODE/_HELPERS/unsorted.dm b/code/_BLUEMOONCODE/_HELPERS/unsorted.dm
deleted file mode 100644
index 438c32a968bb..000000000000
--- a/code/_BLUEMOONCODE/_HELPERS/unsorted.dm
+++ /dev/null
@@ -1,45 +0,0 @@
-/**
- * HTTP Get (Powered by RUSTG)
- *
- * This proc should be used as a replacement for [/world/proc/Export] due to an underlying issue with it.
- * See: https://www.byond.com/forum/post/2772166
- * The one thing you will need to be aware of is that this no longer wraps the response inside a "file", so anything that relies on a file2text() unwrap will need tweaking.
- * RUST HTTP also has better support for HTTPS as well as weird quirks with modern webservers.
- * Returns an assoc list that follows the standard [/world/proc/Export] format (https://secure.byond.com/docs/ref/index.html#/world/proc/Export), with the above exception
- *
- * Arguments:
- * * url - URL to GET
- */
-/proc/HTTPGet(url)
- var/datum/http_request/req = new
- req.prepare(RUSTG_HTTP_METHOD_GET, url)
- req.begin_async()
-
- // Check if we are complete
- UNTIL(req.is_complete())
- var/datum/http_response/res = req.into_response()
-
- if(res.errored)
- . = list() // Return an empty list
- CRASH("Internal error during HTTP get: [res.error]")
-
- var/list/output = list()
- output["STATUS"] = res.status_code
-
- // Handle changes of line format. ASCII 13 = CR
- var/content = replacetext(res.body, "[ascii2text(13)]\n", "\n")
- output["CONTENT"] = content
-
- return output
-
-/proc/log_connection(ckey, ip, cid, connection_type)
- ASSERT(connection_type in list(CONNECTION_TYPE_ESTABLISHED, CONNECTION_TYPE_DROPPED_IPINTEL, CONNECTION_TYPE_DROPPED_BANNED, CONNECTION_TYPE_DROPPED_INVALID))
- var/datum/db_query/query_accesslog = SSdbcore.NewQuery("INSERT INTO connection_ipintel_log (`datetime`, `ckey`, `ip`, `computerid`, `result`, `server_id`) VALUES(Now(), :ckey, INET_ATON(:ip), :cid, :result, :server_id)", list(
- "ckey" = ckey,
- "ip" = "[ip ? ip : "127.0.0.1"]",
- "cid" = cid,
- "result" = connection_type,
- "server_id" = CONFIG_GET(string/servername)
- ))
- query_accesslog.warn_execute()
- qdel(query_accesslog)
diff --git a/code/__BLUEMOONCODE/_DEFINES/ipintel.dm b/code/__BLUEMOONCODE/_DEFINES/ipintel.dm
index 781fd4bb277e..9fbc14ae40db 100644
--- a/code/__BLUEMOONCODE/_DEFINES/ipintel.dm
+++ b/code/__BLUEMOONCODE/_DEFINES/ipintel.dm
@@ -1,9 +1,15 @@
-// Connection types. These match enums in the SQL DB. Dont change them
-/// Client was let into the server
-#define CONNECTION_TYPE_ESTABLISHED "ESTABLISHED"
-/// Client was disallowed due to IPIntel
-#define CONNECTION_TYPE_DROPPED_IPINTEL "DROPPED - IPINTEL"
-/// Client was disallowed due to being banned
-#define CONNECTION_TYPE_DROPPED_BANNED "DROPPED - BANNED"
-/// Client was disallowed due to invalid data
-#define CONNECTION_TYPE_DROPPED_INVALID "DROPPED - INVALID"
+#define IPINTEL_RATE_LIMIT_MINUTE "minute"
+#define IPINTEL_RATE_LIMIT_DAY "day"
+
+/// An internal error occurred and the query cannot be processed
+#define IPINTEL_UNKNOWN_INTERNAL_ERROR "unknown_internal_error"
+/// An error occurred with the query and the result is unknown
+#define IPINTEL_UNKNOWN_QUERY_ERROR "unknown_query_error"
+/// Cannot query as we are rate limited for the rest of the day
+#define IPINTEL_RATE_LIMITED_DAY "rate_limited_day"
+/// Cannot query as we are rate limited for the rest of the minute
+#define IPINTEL_RATE_LIMITED_MINUTE "rate_limited_minute"
+/// The IP address is a VPN or bad IP
+#define IPINTEL_BAD_IP "bad_ip"
+/// The IP address is not a VPN or bad IP
+#define IPINTEL_GOOD_IP "good_ip"
diff --git a/code/controllers/configuration/bluemoon_entries/general.dm b/code/controllers/configuration/bluemoon_entries/general.dm
deleted file mode 100644
index cf8b8566a57a..000000000000
--- a/code/controllers/configuration/bluemoon_entries/general.dm
+++ /dev/null
@@ -1,41 +0,0 @@
-/// Defines whether or not mentors can see ckeys alongside mobnames.
-/datum/config_entry/flag/mentors_mobname_only
-
-/// Defines whether the server uses the legacy mentor system with mentors.txt or the SQL system.
-/datum/config_entry/flag/mentor_legacy_system
- protection = CONFIG_ENTRY_LOCKED
-
-/datum/config_entry/flag/ipintel_enabled
- default = FALSE
-
-/datum/config_entry/string/ipintel_domain
- default = "check.getipintel.net"
-
-/datum/config_entry/string/details_url
- default = "https://iphub.info/?ip="
-
-/datum/config_entry/string/contact_email
-
-/datum/config_entry/string/contact_email/ValidateAndSet(str_val)
- return str_val != "ch@nge.me" && (!length(str_val) || findtext(str_val, "@")) && ..()
-
-/datum/config_entry/number/bad_rating
- default = 0.9
- integer = FALSE
- min_val = 0
- max_val = 1
-
-/datum/config_entry/flag/whitelist_mode
- default = TRUE
-
-/datum/config_entry/number/hours_save_good
- default = 72
- min_val = 0
-
-/datum/config_entry/number/hours_save_bad
- default = 24
- min_val = 0
-
-/datum/config_entry/number/playtime_ignore_threshold
- default = 10
- min_val = 0
diff --git a/code/controllers/subsystem/ipintel.dm b/code/controllers/subsystem/ipintel.dm
index a7f5da72de84..6aa4464fecc8 100644
--- a/code/controllers/subsystem/ipintel.dm
+++ b/code/controllers/subsystem/ipintel.dm
@@ -1,3 +1,4 @@
+/* // BLUEMOON EDIT:START IPINTEL FROM TG
SUBSYSTEM_DEF(ipintel)
name = "XKeyScore"
init_order = INIT_ORDER_XKEYSCORE
@@ -11,3 +12,4 @@ SUBSYSTEM_DEF(ipintel)
/datum/controller/subsystem/ipintel/Initialize(timeofday, zlevel)
enabled = TRUE
. = ..()
+*/ // BLUEMOON EDIT:END IPINTEL FROM TG
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index 32883d77500a..825e2d83c787 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -6,7 +6,7 @@
#define STICKYBAN_MAX_EXISTING_USER_MATCHES 5 //ie, users who were connected before the ban triggered
#define STICKYBAN_MAX_ADMIN_MATCHES 2
-/world/IsBanned(key,address,computer_id,type,real_bans_only=FALSE, check_ipintel = TRUE, log_info = TRUE) // BLUEMOON EDIT:START IPINTEL FROM TG
+/world/IsBanned(key,address,computer_id,type,real_bans_only=FALSE)
var/static/key_cache = list()
if(!real_bans_only)
if(key_cache[key] >= REALTIMEOFDAY)
@@ -17,17 +17,13 @@
if(real_bans_only)
key_cache[key] = 0
return FALSE
- log_access("Login (invalid data): [key] [address]-[computer_id]")
+ log_access("Failed Login (invalid data): [key] [address]-[computer_id]")
key_cache[key] = 0
- if(log_info) // BLUEMOON EDIT:START IPINTEL FROM TG
- INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), (ckey(key) || ""), (address || ""), (computer_id || ""), CONNECTION_TYPE_DROPPED_INVALID)
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided invalid or blank information to the server on connection (byond username, IP, and Computer ID.) Provided information for reference: Username:'[key]' IP:'[address]' Computer ID:'[computer_id]'. (If you continue to get this error, please restart byond or contact byond support.)")
if (text2num(computer_id) == 2147483647) //this cid causes stickybans to go haywire
- log_access("Login (invalid cid): [key] [address]-[computer_id]")
+ log_access("Failed Login (invalid cid): [key] [address]-[computer_id]")
key_cache[key] = 0
- if(log_info) // BLUEMOON EDIT:START IPINTEL FROM TG
- INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_INVALID)
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided an invalid Computer ID.)")
if (type == "world")
@@ -52,10 +48,8 @@
message_admins("The admin [key] has been allowed to bypass the whitelist")
addclientmessage(ckey,"You have been allowed to bypass the whitelist")
else
- log_access("Login: [key] - Not on whitelist")
+ log_access("Failed Login: [key] - Not on whitelist")
key_cache[key] = 0
- if(log_info) // BLUEMOON EDIT:START IPINTEL FROM TG
- INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_IPINTEL)
return list("reason"="whitelist", "desc" = "\nReason: You are not on the white list for this server")
//Guest Checking
@@ -63,14 +57,10 @@
if (CONFIG_GET(flag/guest_ban))
log_access("Failed Login: [key] - Guests not allowed")
key_cache[key] = 0
- if(log_info) // BLUEMOON EDIT:START IPINTEL FROM TG
- INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_BANNED)
return list("reason"="guest", "desc"="\nReason: Guests not allowed. Please sign in with a byond account.")
if (CONFIG_GET(flag/panic_bunker) && SSdbcore.Connect())
log_access("Failed Login: [key] - Guests not allowed during panic bunker")
key_cache[key] = 0
- if(log_info) // BLUEMOON EDIT:START IPINTEL FROM TG
- INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_BANNED)
return list("reason"="guest", "desc"="\nReason: Sorry but the server is currently not accepting connections from never before seen players or guests. If you have played on this server with a byond account before, please log in to the byond account you have played from.")
//Population Cap Checking
@@ -80,16 +70,6 @@
key_cache[key] = 0
return list("reason"="popcap", "desc"= "\nReason: [CONFIG_GET(string/extreme_popcap_message)]")
- // BLUEMOON EDIT:START IPINTEL FROM TG
- //check if the IP address is a known proxy/vpn, and the user is not whitelisted
- if(check_ipintel && CONFIG_GET(string/contact_email) && CONFIG_GET(flag/whitelist_mode) && GLOB.ipintel_manager.ipintel_is_banned(key, address))
- log_admin("Failed Login: [key] [computer_id] [address] - Proxy/VPN")
- var/mistakemessage = ""
- if(CONFIG_GET(string/banappeals))
- mistakemessage = "\nIf you have to use one, request whitelisting at: [CONFIG_GET(string/banappeals)]"
- return list("reason"="using proxy or vpn", "desc"="\nReason: Proxies/VPNs are not allowed here. [mistakemessage]")
- // BLUEMOON EDIT:END IPINTEL FROM TG
-
if(CONFIG_GET(flag/ban_legacy_system))
//Ban Checking
@@ -102,8 +82,6 @@
else
log_access("Failed Login: [key] [computer_id] [address] - Banned [.["reason"]]")
key_cache[key] = 0
- if(log_info) // BLUEMOON EDIT: IPINTEL FROM TG
- INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_BANNED)
return .
else
@@ -176,8 +154,6 @@
log_access("Failed Login: [key] [computer_id] [address] - Banned (#[banid]) [.["reason"]]")
qdel(query_ban_check)
key_cache[key] = 0
- if(log_info) // BLUEMOON EDIT IPINTEL FROM TG
- INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(log_connection), ckey(key), address, computer_id, CONNECTION_TYPE_DROPPED_BANNED)
return .
qdel(query_ban_check)
@@ -256,7 +232,7 @@
var/desc = "\nReason:(StickyBan) You, or another user of this computer or connection ([bannedckey]) is banned from playing here. The ban reason is:\n[ban["message"]]\nThis ban was applied by [ban["admin"]]\nThis is a BanEvasion Detection System ban, if you think this ban is a mistake, please wait EXACTLY 6 seconds, then try again before filing an appeal.\n"
. = list("reason" = "Stickyban", "desc" = desc)
- log_access("Login: [key] [computer_id] [address] - StickyBanned [ban["message"]] Target Username: [bannedckey] Placed by [ban["admin"]]")
+ log_access("Failed Login: [key] [computer_id] [address] - StickyBanned [ban["message"]] Target Username: [bannedckey] Placed by [ban["admin"]]")
key_cache[key] = 0
return .
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index b9cee19d54d3..4254acce8536 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -58,7 +58,6 @@ GLOBAL_PROTECT(admin_verbs_admin)
/client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/
/client/proc/game_panel, /*game panel, allows to change game-mode etc*/
/client/proc/mail_panel, /*BLUEMOON ADD - панель управления почтой*/
- /datum/admins/proc/vpn_whitelist, /*BLUEMOON ADD - Допуск */
/client/proc/fax_panel, /*send a paper to fax*/
// /client/proc/sendmob, /*sends a mob somewhere*/ -Removed due to it needing two sorting procs to work, which were executed every time an admin right-clicked. ~Errorage
/client/proc/jumptoarea,
diff --git a/code/modules/admin/ipintel.dm b/code/modules/admin/ipintel.dm
index 687ee92410db..ac2d39b98e2a 100644
--- a/code/modules/admin/ipintel.dm
+++ b/code/modules/admin/ipintel.dm
@@ -136,380 +136,3 @@
log_game("IPINTEL: [text]")
debug_admins("IPINTEL: [text]")
*/ // BLUEMOON EDIT:END IPINTEL FROM TG
-
-// BLUEMOON EDIT:START IPINTEL FROM TG
-GLOBAL_DATUM_INIT(ipintel_manager, /datum/ipintel_manager, new())
-
-/datum/ipintel_manager
- var/throttle = 0
- var/errors = 0
-
- var/list/cache = list()
-
-// Represents an IP intel holder datum
-/datum/ipintel
- /// The IP being checked
- var/ip
- /// The current rating, 0-1 float.
- var/intel = 0
- /// Whether this was loaded from the cache or not
- var/cache = FALSE
- /// How many minutes ago it was cached
- var/cacheminutesago = 0
- /// The date it was cached
- var/cachedate = ""
- /// The real time it was cached
- var/cacherealtime = 0
-
-/datum/ipintel/New()
- cachedate = SQLtime()
- cacherealtime = world.realtime
-
-/datum/ipintel/proc/is_valid()
- . = FALSE
- if(intel < 0)
- return
- if(intel <= CONFIG_GET(number/bad_rating))
- if(world.realtime < cacherealtime + (CONFIG_GET(number/hours_save_good) HOURS))
- return TRUE
- else
- if(world.realtime < cacherealtime + (CONFIG_GET(number/hours_save_bad) HOURS))
- return TRUE
-
-
-
-/**
- * Get IP intel
- *
- * Performs a lookup of the rating for an IP provided
- *
- * Arguments:
- * * ip - The IP to lookup
- * * bypasscache - Do we want to bypass the DB cache?
- * * updatecache - Do we want to update the DB cache?
- */
-/datum/ipintel_manager/proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE)
- var/datum/ipintel/res = new()
- res.ip = ip
- . = res
- if(!ip || !CONFIG_GET(string/contact_email) || !CONFIG_GET(flag/ipintel_enabled))
- return
- if(!bypasscache)
- var/datum/ipintel/cachedintel = cache[ip]
- if(cachedintel && cachedintel.is_valid())
- cachedintel.cache = TRUE
- return cachedintel
-
- if(SSdbcore.IsConnected())
- var/datum/db_query/query_get_ip_intel = SSdbcore.NewQuery({"
- SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW())
- FROM ipintel
- WHERE
- ip = INET_ATON(:ip)
- AND ((
- intel < :rating_bad
- AND
- date + INTERVAL :save_good HOUR > NOW()
- ) OR (
- intel >= :rating_bad
- AND
- date + INTERVAL :save_bad HOUR > NOW()
- ))
- "}, list(
- "ip" = ip,
- "rating_bad" = CONFIG_GET(number/bad_rating),
- "save_good" = CONFIG_GET(number/hours_save_good),
- "save_bad" = CONFIG_GET(number/hours_save_bad),
- ))
- if(!query_get_ip_intel.warn_execute())
- qdel(query_get_ip_intel)
- return
- if(query_get_ip_intel.NextRow())
- res.cache = TRUE
- res.cachedate = query_get_ip_intel.item[1]
- res.intel = text2num(query_get_ip_intel.item[2])
- res.cacheminutesago = text2num(query_get_ip_intel.item[3])
- res.cacherealtime = world.realtime - (text2num(query_get_ip_intel.item[3])*10*60)
- cache[ip] = res
- qdel(query_get_ip_intel)
- return
- qdel(query_get_ip_intel)
- res.intel = ip_intel_query(ip)
- if(updatecache && res.intel >= 0)
- cache[ip] = res
- if(SSdbcore.IsConnected())
- var/datum/db_query/query_add_ip_intel = SSdbcore.NewQuery({"
- INSERT INTO ipintel (ip, intel) VALUES (INET_ATON(:ip), :intel)
- ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()"},
- list(
- "ip" = ip,
- "intel" = res.intel
- )
- )
- query_add_ip_intel.warn_execute()
- qdel(query_add_ip_intel)
-
-
-
-/**
- * Performs the remote IPintel lookup
- *
- *
- *
- * Arguments:
- * * ip - The IP to lookup
- * * retried - Was this attempt retried?
- */
-/datum/ipintel_manager/proc/ip_intel_query(ip, retried = FALSE)
- . = -1 //default
- if(!ip)
- return
- if(throttle > world.timeofday)
- return
-
- // Do not refactor this to use SShttp, because that requires the subsystem to be firing for requests to be made, and this will be triggered before the MC has finished loading
- var/list/http[] = HTTPGet("http://[CONFIG_GET(string/ipintel_domain)]/check.php?ip=[ip]&contact=[CONFIG_GET(string/contact_email)]&format=json&flags=b")
-
- if(http)
- var/status = text2num(http["STATUS"])
-
- if(status == 200)
- var/response = json_decode(http["CONTENT"])
- if(response)
- if(response["status"] == "success")
- var/intelnum = text2num(response["result"])
- if(isnum(intelnum))
- return text2num(response["result"])
- else
- ipintel_handle_error("Bad intel from server: [response["result"]].", ip, retried)
- if(!retried)
- sleep(25)
- return .(ip, 1)
- else
- ipintel_handle_error("Bad response from server: [response["status"]].", ip, retried)
- if(!retried)
- sleep(25)
- return .(ip, 1)
-
- else if(status == 429)
- ipintel_handle_error("Error #429: We have exceeded the rate limit.", ip, 1)
- return
- else
- ipintel_handle_error("Unknown status code: [status].", ip, retried)
- if(!retried)
- sleep(25)
- return .(ip, 1)
- else
- ipintel_handle_error("Unable to connect to API.", ip, retried)
- if(!retried)
- sleep(25)
- return .(ip, 1)
-
-
-
-/**
- * Error handler
- *
- * Handles an IP intel error, also throttling the susbystem if required
- *
- * Arguments:
- * * error - The error description
- * * ip - The IP that was tried
- * * retried - Was this on a retried attempt
- */
-/datum/ipintel_manager/proc/ipintel_handle_error(error, ip, retried)
- if(retried)
- errors++
- error += " Could not check [ip]. Disabling IPINTEL for [errors] minute[(errors == 1 ? "" : "s")]"
- throttle = world.timeofday + (2 * errors MINUTES)
- else
- error += " Attempting retry on [ip]."
- log_ipintel(error)
-
-
-
-/**
- * Logs an IPintel error
- *
- * Pretty self explanatory. Logs errors regarding ipintel.
- *
- * Arguments:
- * * text - Argument 1
- */
-/datum/ipintel_manager/proc/log_ipintel(text)
- log_game("IPINTEL: [text]")
- log_access("IPINTEL: [text]")
-
-
-
-/**
- * IPIntel Ban Checker
- *
- * Checks if a user is banned due to IPintel. It will check configuration, DB, whitelist checks, and more
- *
- * Arguments:
- * * t_ckey - The ckey to check
- * * t_ip - The IP to check
- */
-/datum/ipintel_manager/proc/ipintel_is_banned(t_ckey, t_ip)
- if(!CONFIG_GET(string/contact_email))
- return FALSE
- if(!CONFIG_GET(flag/ipintel_enabled))
- return FALSE
- if(!CONFIG_GET(flag/whitelist_mode))
- return FALSE
- if(!SSdbcore.IsConnected())
- return FALSE
- if(!ipintel_badip_check(t_ip))
- return FALSE
- if(vpn_whitelist_check(t_ckey))
- return FALSE
- return TRUE
-
-
-
-/**
- * IP Rating Checker
- *
- * Checks if a provided IP passes the config threshold for denial
- *
- * Arguments:
- * * target_ip - The IP to check
- */
-/datum/ipintel_manager/proc/ipintel_badip_check(target_ip)
- var/rating_bad = CONFIG_GET(number/bad_rating)
- if(!rating_bad)
- log_access("ipintel_badip_check reports misconfigured rating_bad directive")
- return FALSE
- var/valid_hours = CONFIG_GET(number/hours_save_bad)
- if(!valid_hours)
- log_access("ipintel_badip_check reports misconfigured ipintel_save_bad directive")
- return FALSE
- var/datum/db_query/query_get_ip_intel = SSdbcore.NewQuery({"
- SELECT * FROM ipintel WHERE ip = INET_ATON(:target_ip)
- AND intel >= :rating_bad AND (date + INTERVAL :valid_hours HOUR) > NOW()"},
- list(
- "target_ip" = target_ip,
- "rating_bad" = rating_bad,
- "valid_hours" = valid_hours
- )
- )
- if(!query_get_ip_intel.warn_execute())
- log_access("ipintel_badip_check reports failed query execution")
- qdel(query_get_ip_intel)
- return FALSE
- if(!query_get_ip_intel.NextRow())
- qdel(query_get_ip_intel)
- return FALSE
- qdel(query_get_ip_intel)
- return TRUE
-
-
-
-/**
- * VPN whitelist checker
- *
- * Checks if a ckey is whitelisted to be using a VPN against the DB
- *
- * Arguments:
- * * target_ckey - The ckey to check
- */
-/datum/ipintel_manager/proc/vpn_whitelist_check(target_ckey)
- if(!CONFIG_GET(flag/whitelist_mode))
- return FALSE
- var/datum/db_query/query_whitelist_check = SSdbcore.NewQuery("SELECT * FROM vpn_whitelist WHERE ckey=:ckey", list(
- "ckey" = target_ckey
- ))
- if(!query_whitelist_check.warn_execute())
- qdel(query_whitelist_check)
- return FALSE
- if(query_whitelist_check.NextRow())
- qdel(query_whitelist_check)
- return TRUE // At least one row in the whitelist names their ckey. That means they are whitelisted.
- qdel(query_whitelist_check)
- return FALSE
-
-
-
-/**
- * VPN whitelist adder
- *
- * Adds a ckey to the VPN whitelist. Asks the admin to also provide a link to their request.
- *
- * Arguments:
- * * target_ckey - The ckey to whitelist
- */
-/datum/ipintel_manager/proc/vpn_whitelist_add(target_ckey)
- var/reason_string = input(usr, "Enter link to the URL of their whitelist request on the forum.","Reason required") as message|null
- if(!reason_string)
- return FALSE
- var/datum/db_query/query_whitelist_add = SSdbcore.NewQuery("INSERT INTO vpn_whitelist (ckey,reason) VALUES (:targetckey, :reason)", list(
- "targetckey" = target_ckey,
- "reason" = reason_string
- ))
- if(!query_whitelist_add.warn_execute())
- qdel(query_whitelist_add)
- return FALSE
- qdel(query_whitelist_add)
- return TRUE
-
-
-
-/**
- * VPN whitelist remover
- *
- * Removes a ckey from the VPN whitelist. Pretty simple.
- *
- * Arguments:
- * * target_ckey - The ckey to remove
- */
-/datum/ipintel_manager/proc/vpn_whitelist_remove(target_ckey)
- var/datum/db_query/query_whitelist_remove = SSdbcore.NewQuery("DELETE FROM vpn_whitelist WHERE ckey=:targetckey", list(
- "targetckey" = target_ckey
- ))
- if(!query_whitelist_remove.warn_execute())
- qdel(query_whitelist_remove)
- return FALSE
- qdel(query_whitelist_remove)
- return TRUE
-
-
-
-/**
- * VPN whitelist panel
- *
- * Doesnt actually open a panel, this is just a verb to handle the rest of the whitelist operations
- *
- * Arguments:
- * * target_ckey - The ckey to add/remove
- */
-/datum/ipintel_manager/proc/vpn_whitelist_panel(target_ckey as text)
- if(!check_rights(R_ADMIN))
- return
- if(!target_ckey)
- return
- var/is_already_whitelisted = vpn_whitelist_check(target_ckey)
- if(is_already_whitelisted)
- var/confirm = alert("[target_ckey] is already whitelisted. Remove them?", "Confirm Removal", "No", "Yes")
- if(!confirm || confirm != "Yes")
- to_chat(usr, "VPN whitelist alteration cancelled.")
- return
- else if(vpn_whitelist_remove(target_ckey))
- to_chat(usr, "[target_ckey] was removed from the VPN whitelist.")
- else
- to_chat(usr, "VPN whitelist unchanged.")
- else
- if(vpn_whitelist_add(target_ckey))
- to_chat(usr, "[target_ckey] was added to the VPN whitelist.")
- else
- to_chat(usr, "VPN whitelist unchanged.")
-
-/datum/admins/proc/vpn_whitelist()
- set category = "Admin"
- set name = "VPN Ckey Whitelist"
- if(!check_rights(R_BAN))
- return
- var/key = stripped_input(usr, "Enter ckey to add/remove, or leave blank to cancel:", "VPN Whitelist add/remove", max_length=32)
- if(key)
- GLOB.ipintel_manager.vpn_whitelist_panel(key)
-// BLUEMOON EDIT:END IPINTEL FROM TG
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 6c912111628f..ef0edc1a6323 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -458,10 +458,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (CONFIG_GET(flag/irc_first_connection_alert))
send2tgs_adminless_only("new_byond_user", "[key_name(src)] (IP: [address], ID: [computer_id]) is a new BYOND account [account_age] day[(account_age==1?"":"s")] old, created on [account_join_date].")
get_message_output("watchlist entry", ckey)
- //check_ip_intel() // BLUEMOON EDIT: IPINTEL FROM TG
+ check_ip_intel()
validate_key_in_db()
- check_ip_intel() // BLUEMOON EDIT: IPINTEL FROM TG
send_resources()
update_clickcatcher()
@@ -515,6 +514,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
menuitem.Load_checked(src)
SSambience.remove_ambience_client(src)
+
view_size = new(src, getScreenSize(prefs.widescreenpref))
view_size.resetFormat()
view_size.setZoomMode()
@@ -883,45 +883,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
ip_intel = res.intel
*/ // BLUEMOON EDIT:END IPINTEL FROM TG
-// BLUEMOON EDIT:START IPINTEL FROM TG
-/client/proc/check_ip_intel()
- set waitfor = 0 //we sleep when getting the intel, no need to hold up the client connection while we sleep
- if(CONFIG_GET(flag/ipintel_enabled))
- if(CONFIG_GET(number/playtime_ignore_threshold) && CONFIG_GET(flag/use_exp_tracking))
- var/living_hours = text2num(prefs.exp[EXP_TYPE_LIVING]) / 60
- if(living_hours >= CONFIG_GET(number/playtime_ignore_threshold))
- return
-
- if(is_connecting_from_localhost())
- log_access("check_ip_intel: skip check for player [key_name_admin(src)] connecting from localhost.")
- return
-
- if(GLOB.ipintel_manager.vpn_whitelist_check(ckey))
- log_access("check_ip_intel: skip check for player [key_name_admin(src)] [address] on whitelist.")
- return
-
- var/datum/ipintel/res = GLOB.ipintel_manager.get_ip_intel(address)
- ip_intel = res.intel
- verify_ip_intel()
-
-/client/proc/verify_ip_intel()
- if(ip_intel >= CONFIG_GET(number/bad_rating))
- var/detailsurl = CONFIG_GET(string/details_url) ? "(IP Info)" : ""
- if(CONFIG_GET(flag/whitelist_mode))
- // Do not move this to isBanned(). This may sound weird, but:
- // This needs to happen after their account is put into the DB
- // This way, admins can then note people
- spawn(40) // This is necessary because without it, they won't see the message, and addtimer cannot be used because the timer system may not have initialized yet
- message_admins("IPIntel: [key_name_admin(src)] on IP [address] was rejected. [detailsurl]")
- var/blockmsg = "Error: proxy/VPN detected. Proxy/VPN use is not allowed here. Deactivate it before you reconnect."
- if(CONFIG_GET(string/banappeals))
- blockmsg += "\nIf you are not actually using a proxy/VPN, or have no choice but to use one, request whitelisting at: [CONFIG_GET(string/banappeals)]"
- to_chat(src, blockmsg)
- qdel(src)
- else
- message_admins("IPIntel: [key_name_admin(src)] on IP [address] is likely to be using a Proxy/VPN. [detailsurl]")
-// BLUEMOON EDIT:END IPINTEL FROM TG
-
/client/Click(atom/object, atom/location, control, params, ignore_spam = FALSE, extra_info)
if(last_click > world.time - world.tick_lag)
return
diff --git a/config/bluemoon/ipintel.txt b/config/bluemoon/ipintel.txt
index 847274bbee4e..4bd48ad54235 100644
--- a/config/bluemoon/ipintel.txt
+++ b/config/bluemoon/ipintel.txt
@@ -1,21 +1,22 @@
## IPINTEL:
-## This section contains all the information for IPIntel (The Anti VPN system)
## This allows you to detect likely proxies by checking ips against getipintel.net
-# Enable or disable IPIntel entirely
-#IPINTEL_ENABLED TRUE
-# Whitelist mode. If true, people on proxies/VPNs will need whitelisting if they arent past the threshold below. If false, admins are merely warned with no automatic action
-#WHITELIST_MODE FALSE
-# Threshold to kick people out (0-1 percentage float)
-#BAD_RATING 0.98
-# Contact email (required, leaving blank disables this)
-#CONTACT_EMAIL "ss13bluemoon@gmail.com"
-# How many hours to save good matches for (IPIntel has rate limits)
-#HOURS_SAVE_GOOD 72
-# How many hours to save bad matches for (IPIntel has rate limits)
-#HOURS_SAVE_BAD 24
-# IPIntel Domain. Do not put http:// in front of it
-#IPINTEL_DOMAIN "check.getipintel.net"
-## Details URL for more info on an IP (such as ASN). IP is tacked on the end.
-#DETAILS_URL "https://iphub.info/?ip="
-# Ignore checking IPs with more hours than the threshold below. Requires EXP tracking to be enabled
-#PLAYTIME_IGNORE_THRESHOLD 90
+## Rating to warn at: (0.8 is good, 1 is 100% likely to be a spammer/proxy, 0.8 is 80%, etc) anything equal to or higher then this number triggers an admin warning
+#IPINTEL_RATING_BAD 0.8
+## Contact email, (required to use the service, leaving blank or default disables IPINTEL)
+#IPINTEL_EMAIL ch@nge.me
+## Query base, if you pay for more queries this is what you want to change.
+#IPINTEL_BASE check.getipintel.net
+## Maximum number of queries in a minute
+#IPINTEL_MAX_QUERY_MINUTE 15
+## Maximum number of queries in a day
+#IPINTEL_MAX_QUERY_DAY 500
+## Whether clients which cannot be checked due to a rate limit will be denied
+#IPINTEL_REJECT_RATE_LIMITED
+## Whether clients which are flagged as a VPN will be denied
+IPINTEL_REJECT_BAD
+## Whether clients which cannot be checked due to an error of some form will be denied
+#IPINTEL_REJECT_UNKNOWN
+## How long to store results in the cache before they must be retrieved again. IN DAYS.
+#IPINTEL_CACHE_LENGTH 7
+## How many minutes of living playtime to be automatically exempt from IPIntel. 0 for never.
+#IPINTEL_EXEMPT_PLAYTIME_LIVING 0
diff --git a/modular_bluemoon/phoenix404/code/controllers/configuration/entries/bluemoon_config_entries.dm b/modular_bluemoon/phoenix404/code/controllers/configuration/entries/bluemoon_config_entries.dm
new file mode 100644
index 000000000000..3894e3cdf788
--- /dev/null
+++ b/modular_bluemoon/phoenix404/code/controllers/configuration/entries/bluemoon_config_entries.dm
@@ -0,0 +1,41 @@
+/// Defines whether or not mentors can see ckeys alongside mobnames.
+/datum/config_entry/flag/mentors_mobname_only
+
+/// Defines whether the server uses the legacy mentor system with mentors.txt or the SQL system.
+/datum/config_entry/flag/mentor_legacy_system
+ protection = CONFIG_ENTRY_LOCKED
+
+/datum/config_entry/string/ipintel_base
+ default = "check.getipintel.net"
+
+/datum/config_entry/string/ipintel_email
+
+/datum/config_entry/string/ipintel_email/ValidateAndSet(str_val)
+ return str_val != "ch@nge.me" && (!length(str_val) || findtext(str_val, "@")) && ..()
+
+/datum/config_entry/number/ipintel_rating_bad
+ default = 1
+ integer = FALSE
+ min_val = 0
+ max_val = 1
+
+/datum/config_entry/flag/ipintel_reject_rate_limited
+ default = FALSE
+
+/datum/config_entry/flag/ipintel_reject_bad
+ default = FALSE
+
+/datum/config_entry/flag/ipintel_reject_unknown
+ default = FALSE
+
+/datum/config_entry/number/ipintel_rate_minute
+ default = 15
+ min_val = 0
+
+/datum/config_entry/number/ipintel_cache_length
+ default = 7
+ min_val = 0
+
+/datum/config_entry/number/ipintel_exempt_playtime_living
+ default = 5
+ min_val = 0
diff --git a/modular_bluemoon/phoenix404/code/controllers/subsystem/ipintel.dm b/modular_bluemoon/phoenix404/code/controllers/subsystem/ipintel.dm
new file mode 100644
index 000000000000..236e781168d0
--- /dev/null
+++ b/modular_bluemoon/phoenix404/code/controllers/subsystem/ipintel.dm
@@ -0,0 +1,301 @@
+SUBSYSTEM_DEF(ipintel)
+ name = "XKeyScore"
+ init_order = INIT_ORDER_XKEYSCORE
+ flags = SS_NO_INIT|SS_NO_FIRE
+ /// The threshold for probability to be considered a VPN and/or bad IP
+ var/probability_threshold
+
+ /// Cache for previously queried IP addresses and those stored in the database
+ var/list/datum/ip_intel/cached_queries = list()
+ /// The store for rate limiting
+ var/rate_limit_minute
+
+/// The ip intel for a given address
+/datum/ip_intel
+ /// If this intel was just queried, the status of the query
+ var/query_status
+ var/result
+ var/address
+ var/date
+
+/datum/controller/subsystem/ipintel/OnConfigLoad()
+ var/list/fail_messages = list()
+
+ var/contact_email = CONFIG_GET(string/ipintel_email)
+
+ if(!length(contact_email))
+ fail_messages += "No contact email"
+
+ if(!findtext(contact_email, "@"))
+ fail_messages += "Invalid contact email"
+
+ if(!length(CONFIG_GET(string/ipintel_base)))
+ fail_messages += "Invalid query base"
+
+ if (!CONFIG_GET(flag/sql_enabled))
+ fail_messages += "The database is not enabled"
+
+ if(length(fail_messages))
+ message_admins("IPIntel: Initialization failed check logs!")
+ WRITE_LOG(GLOB.world_suspicious_login_log, "IPIntel is not enabled because the configs are not valid. [fail_messages]")
+
+/datum/controller/subsystem/ipintel/stat_entry(msg)
+ return "[..()] | M: [CONFIG_GET(number/ipintel_rate_minute) - rate_limit_minute]"
+
+
+/datum/controller/subsystem/ipintel/proc/is_enabled()
+ return length(CONFIG_GET(string/ipintel_email)) && length(CONFIG_GET(string/ipintel_base)) && CONFIG_GET(flag/sql_enabled)
+
+/datum/controller/subsystem/ipintel/proc/get_address_intel_state(address, probability_override)
+ if (!is_enabled())
+ return IPINTEL_GOOD_IP
+ var/datum/ip_intel/intel = query_address(address)
+ if(isnull(intel))
+ stack_trace("query_address did not return an ip intel response")
+ return IPINTEL_UNKNOWN_INTERNAL_ERROR
+
+ if(istext(intel))
+ return intel
+
+ if(!(intel.query_status in list("success", "cached")))
+ return IPINTEL_UNKNOWN_QUERY_ERROR
+ var/check_probability = probability_override || CONFIG_GET(number/ipintel_rating_bad)
+ if(intel.result >= check_probability)
+ return IPINTEL_BAD_IP
+ return IPINTEL_GOOD_IP
+
+/datum/controller/subsystem/ipintel/proc/is_rate_limited()
+ var/static/minute_key
+ var/expected_minute_key = floor(REALTIMEOFDAY / 1 MINUTES)
+
+ if(minute_key != expected_minute_key)
+ minute_key = expected_minute_key
+ rate_limit_minute = 0
+
+ if(rate_limit_minute >= CONFIG_GET(number/ipintel_rate_minute))
+ return IPINTEL_RATE_LIMITED_MINUTE
+ return FALSE
+
+/datum/controller/subsystem/ipintel/proc/query_address(address, allow_cached = TRUE)
+ if (!is_enabled())
+ return
+ if(allow_cached && fetch_cached_ip_intel(address))
+ return cached_queries[address]
+ var/is_rate_limited = is_rate_limited()
+ if(is_rate_limited)
+ return is_rate_limited
+ rate_limit_minute += 1
+
+ var/query_base = "https://[CONFIG_GET(string/ipintel_base)]/check.php?ip="
+ var/query = "[query_base][address]&contact=[CONFIG_GET(string/ipintel_email)]&flags=b&format=json"
+
+ var/datum/http_request/request = new
+ request.prepare(RUSTG_HTTP_METHOD_GET, query)
+ request.execute_blocking()
+ var/datum/http_response/response = request.into_response()
+ var/list/data = json_decode(response.body)
+ // Log the response
+ WRITE_LOG(GLOB.query_debug_log, "ip check response body [data]")
+
+ var/datum/ip_intel/intel = new
+ intel.query_status = data["status"]
+ if(intel.query_status != "success")
+ return intel
+ intel.result = data["result"]
+ if(istext(intel.result))
+ intel.result = text2num(intel.result)
+ intel.date = ISOtime()
+ intel.address = address
+ cached_queries[address] = intel
+ add_intel_to_database(intel)
+ return intel
+
+/datum/controller/subsystem/ipintel/proc/add_intel_to_database(datum/ip_intel/intel)
+ set waitfor = FALSE //no need to make the client connection wait for this step.
+ if (!SSdbcore.Connect())
+ return
+ var/datum/db_query/query = SSdbcore.NewQuery(
+ "INSERT INTO [format_table_name("ipintel")] ( \
+ ip, \
+ intel \
+ ) VALUES ( \
+ INET_ATON(:address), \
+ :result \
+ )", list(
+ "address" = intel.address,
+ "result" = intel.result,
+ )
+ )
+ if(!query.warn_execute())
+ qdel(query)
+ return
+ qdel(query)
+
+/datum/controller/subsystem/ipintel/proc/fetch_cached_ip_intel(address)
+ if (!SSdbcore.Connect())
+ return
+ var/ipintel_cache_length = CONFIG_GET(number/ipintel_cache_length)
+ var/date_restrictor = ""
+ var/sql_args = list("address" = address)
+ if(ipintel_cache_length > 1)
+ date_restrictor = " AND date > DATE_SUB(NOW(), INTERVAL :ipintel_cache_length DAY)"
+ sql_args["ipintel_cache_length"] = ipintel_cache_length
+ var/datum/db_query/query = SSdbcore.NewQuery(
+ "SELECT * FROM [format_table_name("ipintel")] WHERE ip = INET_ATON(:address)[date_restrictor]",
+ sql_args
+ )
+ if(!query.warn_execute())
+ qdel(query)
+ return
+
+
+ query.NextRow()
+ var/list/data = query.item
+ qdel(query)
+ if(isnull(data))
+ return null
+
+ var/datum/ip_intel/intel = new
+ intel.query_status = "cached"
+ intel.result = data["intel"]
+ if(istext(intel.result))
+ intel.result = text2num(intel.result)
+ intel.date = data["date"]
+ intel.address = address
+ return TRUE
+
+/datum/controller/subsystem/ipintel/proc/is_exempt(client/player)
+ if(player.holder || GLOB.deadmins[player.ckey])
+ return TRUE
+ var/exempt_living_playtime = CONFIG_GET(number/ipintel_exempt_playtime_living)
+ if(exempt_living_playtime > 0)
+ var/list/play_records = player.prefs.exp
+ if (!play_records.len)
+ player.set_exp_from_db()
+ play_records = player.prefs.exp
+ if(length(play_records) && play_records[EXP_TYPE_LIVING] > exempt_living_playtime)
+ return TRUE
+ return FALSE
+
+/datum/controller/subsystem/ipintel/proc/is_whitelisted(ckey)
+ var/datum/db_query/query = SSdbcore.NewQuery(
+ "SELECT * FROM [format_table_name("ipintel_whitelist")] WHERE ckey = :ckey", list(
+ "ckey" = ckey
+ )
+ )
+ if(!query.warn_execute())
+ qdel(query)
+ return
+ query.NextRow()
+ . = !!query.item // if they have a row, they are whitelisted
+ qdel(query)
+
+/client/proc/ipintel_allow(ckey as text)
+ set category = "Debug"
+ set name = "Whitelist Player VPN"
+ set desc = "Allow a player to connect even if they are using a VPN."
+
+ if (!SSipintel.is_enabled())
+ to_chat(usr, "The ipintel system is not currently enabled but you can still edit the whitelists")
+ if(SSipintel.is_whitelisted(ckey))
+ to_chat(usr, "Player is already whitelisted.")
+ return
+
+ var/datum/db_query/query = SSdbcore.NewQuery(
+ "INSERT INTO [format_table_name("ipintel_whitelist")] ( \
+ ckey, \
+ admin_ckey \
+ ) VALUES ( \
+ :ckey, \
+ :admin_ckey \
+ )", list(
+ "ckey" = ckey,
+ "admin_ckey" = usr.ckey,
+ )
+ )
+ if(!query.warn_execute())
+ qdel(query)
+ return
+
+ qdel(query)
+ message_admins("IPINTEL: [key_name_admin(usr)] has whitelisted '[ckey]'")
+
+/client/proc/ipintel_revoke(ckey as text)
+ set category = "Debug"
+ set name = "Revoke Player VPN Whitelist"
+ set desc = "Revoke a player's VPN whitelist."
+
+ if (!SSipintel.is_enabled())
+ to_chat(usr, "The ipintel system is not currently enabled but you can still edit the whitelists")
+ if(!SSipintel.is_whitelisted(ckey))
+ to_chat(usr, "Player is not whitelisted.")
+ return
+ var/datum/db_query/query = SSdbcore.NewQuery(
+ "DELETE FROM [format_table_name("ipintel_whitelist")] WHERE ckey = :ckey", list(
+ "ckey" = ckey
+ )
+ )
+ if(!query.warn_execute())
+ qdel(query)
+ return
+
+ message_admins("IPINTEL: [key_name_admin(usr)] has revoked the VPN whitelist for '[ckey]'")
+
+/client/proc/check_ip_intel()
+ if (!SSipintel.is_enabled())
+ return
+ if(SSipintel.is_exempt(src) || SSipintel.is_whitelisted(ckey))
+ return
+
+ var/intel_state = SSipintel.get_address_intel_state(address)
+ var/reject_bad_intel = CONFIG_GET(flag/ipintel_reject_bad)
+ var/reject_unknown_intel = CONFIG_GET(flag/ipintel_reject_unknown)
+ var/reject_rate_limited = CONFIG_GET(flag/ipintel_reject_rate_limited)
+
+ var/connection_rejected = FALSE
+ var/datum/ip_intel/intel = SSipintel.cached_queries[address]
+ switch(intel_state)
+ if(IPINTEL_BAD_IP)
+ log_access("IPINTEL: [ckey] was flagged as a VPN with [intel.result * 100]% likelihood.")
+ if(reject_bad_intel)
+ to_chat_immediate(src, span_boldnotice("Your connection has been detected as a VPN."))
+ connection_rejected = TRUE
+ else
+ message_admins("IPINTEL: [key_name_admin(src)] has been flagged as a VPN with [intel.result * 100]% likelihood.")
+
+ if(IPINTEL_RATE_LIMITED_DAY, IPINTEL_RATE_LIMITED_MINUTE)
+ log_access("IPINTEL: [ckey] was unable to be checked due to the rate limit.")
+ if(reject_rate_limited)
+ to_chat_immediate(src, span_boldnotice("New connections are not being allowed at this time."))
+ connection_rejected = TRUE
+ else
+ message_admins("IPINTEL: [key_name_admin(src)] was unable to be checked due to rate limiting.")
+
+ if(IPINTEL_UNKNOWN_INTERNAL_ERROR, IPINTEL_UNKNOWN_QUERY_ERROR)
+ log_access("IPINTEL: [ckey] unable to be checked due to an error.")
+ if(reject_unknown_intel)
+ to_chat_immediate(src, span_boldnotice("Your connection cannot be processed at this time."))
+ connection_rejected = TRUE
+ else
+ message_admins("IPINTEL: [key_name_admin(src)] was unable to be checked due to an error.")
+
+ if(!connection_rejected)
+ return
+
+ var/list/contact_where = list()
+ var/forum_url = CONFIG_GET(string/discordurl)
+ if(forum_url)
+ contact_where += list("Forums")
+ var/appeal_url = CONFIG_GET(string/banappeals)
+ if(appeal_url)
+ contact_where += list("Ban Appeals")
+
+ var/message_string = "Your connection has been rejected at this time. If you believe this is in error or have any questions please contact an admin"
+ if(length(contact_where))
+ message_string += " at [english_list(contact_where)]"
+ else
+ message_string += " somehow."
+ message_string += "."
+
+ to_chat_immediate(src, span_userdanger(message_string))
+ qdel(src)
diff --git a/modular_bluemoon/phoenix404/modules/client/client_procs.dm b/modular_bluemoon/phoenix404/modules/client/client_procs.dm
deleted file mode 100644
index 9469799fc647..000000000000
--- a/modular_bluemoon/phoenix404/modules/client/client_procs.dm
+++ /dev/null
@@ -1,5 +0,0 @@
-/client/proc/is_connecting_from_localhost()
- var/static/list/localhost_addresses = list("127.0.0.1", "::1")
- if((!address && !world.port) || (address in localhost_addresses))
- return TRUE
- return FALSE
diff --git a/tgstation.dme b/tgstation.dme
index 21390adee8c5..bfe892538525 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -343,7 +343,6 @@
#include "code\__SPLURTCODE\DEFINES\arousal\genitals.dm"
#include "code\__SPLURTCODE\DEFINES\dcs\signals.dm"
#include "code\_BLUEMOONCODE\_HELPERS\time.dm"
-#include "code\_BLUEMOONCODE\_HELPERS\unsorted.dm"
#include "code\_globalvars\admin.dm"
#include "code\_globalvars\bitfields.dm"
#include "code\_globalvars\configuration.dm"
@@ -443,7 +442,6 @@
#include "code\controllers\subsystem.dm"
#include "code\controllers\configuration\config_entry.dm"
#include "code\controllers\configuration\configuration.dm"
-#include "code\controllers\configuration\bluemoon_entries\general.dm"
#include "code\controllers\configuration\entries\admin.dm"
#include "code\controllers\configuration\entries\alert.dm"
#include "code\controllers\configuration\entries\antag_rep.dm"
@@ -4548,8 +4546,9 @@
#include "modular_bluemoon\phoenix404\code\entries.dm"
#include "modular_bluemoon\phoenix404\code\_HELPERS\admin.dm"
#include "modular_bluemoon\phoenix404\code\_HELPERS\roundend.dm"
+#include "modular_bluemoon\phoenix404\code\controllers\configuration\entries\bluemoon_config_entries.dm"
+#include "modular_bluemoon\phoenix404\code\controllers\subsystem\ipintel.dm"
#include "modular_bluemoon\phoenix404\modules\admin\code\player_ranks.dm"
-#include "modular_bluemoon\phoenix404\modules\client\client_procs.dm"
#include "modular_bluemoon\phoenix404\modules\mentor\code\mentor.dm"
#include "modular_bluemoon\phoenix404\modules\player_ranks\code\player_rank_controller\_player_rank_controller.dm"
#include "modular_bluemoon\phoenix404\modules\player_ranks\code\player_rank_controller\mentor_controller.dm"
diff --git a/tgui/packages/tgui/interfaces/PlayerPanel2.js b/tgui/packages/tgui/interfaces/PlayerPanel2.js
index 5a3824d68bfb..178452764f86 100644
--- a/tgui/packages/tgui/interfaces/PlayerPanel2.js
+++ b/tgui/packages/tgui/interfaces/PlayerPanel2.js
@@ -724,7 +724,6 @@ const PunishmentActions = (props, context) => {
{data_player_join_date}
{data_byond_version}
{active_role_ban_count}
- {active_role_ban_count}