Files
goonstation-2016/code/admin/bans.dm
drsingh dc4217b498 modified process scheduler, 510 compatibility, explosions moved to process
this is all very alpha please don't hate me too much if i fucked it up
2016-03-12 16:36:00 -08:00

809 lines
30 KiB
Plaintext

/////////////////////////
// BASE BAN PROCS
// Most of these have two steps (instead of double the procs)
// First step sends the initial query to the bans web API
// Second step handles the response
/////////////////////////
//Returns the expiry timestamp in a human readable format
/proc/getExpiry(minutes)
var/CMinutes = (world.realtime / 10) / 60
minutes = text2num(minutes)
var/exp = minutes - CMinutes
if (exp <= 0)
return 0
else
var/timeleftstring
if (exp >= 1440) //1440 = 1 day in minutes
exp = round(exp / 1440, 0.1)
timeleftstring = "[exp] Day[exp > 1 ? "s" : ""]"
else if (exp >= 60) //60 = 1 hour in minutes
exp = round(exp / 60, 0.1)
timeleftstring = "[exp] Hour[exp > 1 ? "s" : ""]"
else
timeleftstring = "[exp] Minute[exp > 1 ? "s" : ""]"
return timeleftstring
//Are ya banned? Well!? ARE YA?!
//A return 0 is a "this guy is ok let him in"
//Anything that returns as true is a "bad dude kill his connection"
//(this doesn't use the 'step' var thing because we need the response here and now)
/proc/checkBan(ckey, compID, ip)
set background = 1
if (!ckey && !compID && !ip)
logTheThing("debug", null, null, "<b>Bans Error</b>: No details passed to <b>checkBan</b>")
logTheThing("diary", null, null, "Bans Error: No details passed to checkBan", "debug")
return 0
if(!ip) return 0 //Ignore if localhost
//Check if this user has a ban already
var/query[] = new()
query["ckey"] = ckey
query["compID"] = compID
query["ip"] = ip
var/data[] = queryAPI("bans/check", query, 1)
//If the server returned nothing, a "no bans found" record, or a "this dude is exception'd"
//Then they are good to go
if (!data || data["none"] || data["exception"])
return 0
if (data["error"]) //Error returned from the API welp
logTheThing("debug", null, null, "<b>Bans Error</b>: Error returned in <b>checkBan</b> for <b>[ckey]</b>: [data["error"]]")
logTheThing("diary", null, null, "Bans Error: Error returned in checkBan for [ckey]: [data["error"]]", "debug")
return 0
//We only care about the latest match for this (so far)
var/list/row = data[data[1]]
/*
for (var/e = 1, e <= data.len, e++) //each ban
var/id = data[e]
var/list/details = data[id]
for (var/i = 1, i <= details.len, i++) //each item for this ban
*/
//Are any of the details...different? This is to catch out ban evading jerks who change their ckey but forget to mask their IP or whatever
var/timeAdded = 0
if (row["ckey"] != ckey || row["ip"] != ip || row["compID"] != compID) //Insert a new ban for this JERK
var/newChain = 0
if (text2num(row["previous"]) > 0) //if we matched a previous auto-added ban
if (text2num(row["chain"]) > 0)
newChain = text2num(row["chain"]) + 1
else //no multiplier present, default to x2 (this should never occur)
newChain = 2
else //didn't match a previous evasion ban, start off with x1
newChain = 1
timeAdded = (row["ckey"] != ckey ? 1 : 0) //only add time if a ckey didnt match, not anything else
var/CMinutes = (world.realtime / 10) / 60
var/remaining = (text2num(row["timestamp"]) > 0 ? text2num(row["timestamp"]) - CMinutes : 0)
var/addData[] = new()
addData["ckey"] = ckey
addData["compID"] = compID
addData["ip"] = ip
addData["reason"] = row["reason"]
addData["oakey"] = row["oakey"]
addData["akey"] = "Auto Banner"
addData["mins"] = remaining
addData["previous"] = text2num(row["id"])
addData["chain"] = newChain
addData["addTime"] = timeAdded
var/rVal = addBan(1, addData)
if (rVal)
logTheThing("debug", null, null, "<b>Bans Error</b>: Add ban in checkBan failed with message <b>[rVal]</b>")
logTheThing("diary", null, null, "Bans Error: Add ban in checkBan failed with message [rVal]", "debug")
var/oakey = (row["oakey"] == "N/A" ? row["akey"] : row["oakey"])
if (text2num(row["timestamp"]) > 0) //Temp ban found, determine if it should expire or not
if (!getExpiry(row["timestamp"])) //It expired! Go you!
var/deleteData[] = new()
deleteData["id"] = row["id"]
deleteData["ckey"] = row["ckey"]
deleteData["compID"] = row["compID"]
deleteData["ip"] = row["ip"]
deleteData["akey"] = "Auto Unbanner"
var/rVal = deleteBan(1, deleteData)
if (rVal)
logTheThing("debug", null, null, "<b>Bans Error</b>: Delete temp ban in checkBan failed with message <b>[rVal]</b>")
logTheThing("diary", null, null, "Bans Error: Delete temp ban in checkBan failed with message [rVal]", "debug")
return 0
else //Temp ban still in effect. DENIED
var/details = "[row["reason"]]<br>"
details += "Banned By: [oakey]<br>"
details += "(This ban will be automatically removed in [getExpiry(row["timestamp"])].)"
details += "[timeAdded ? "<br>(5 days have been automatically added to your ban for attempted ban evasion)" : ""]"
return details
else //Permaban found, the player is DENIED
var/details = "[row["reason"]]<br>"
details += "Banned By: [oakey]<br>"
details += "(This is a permanent ban)"
return details
return 0 //Should never occur
/proc/addBan(step = 1, data)
set background = 1
if (step == 1)
var/banTimestamp = 0
if (data["mins"] > 0) //If a temp ban, calculate expiry
var/CMinutes = (world.realtime / 10) / 60
banTimestamp = (data["previous"] && data["addTime"] ? CMinutes + data["mins"] + 7200 : CMinutes + data["mins"]) //Add 5 days (7200 mins) onto the ban if it's an evasion attempt
var/query[] = new()
query["ckey"] = (data["ckey"] ? data["ckey"] : "N/A")
query["compID"] = (data["compID"] ? data["compID"] : "N/A")
query["ip"] = (data["ip"] ? data["ip"] : "N/A")
query["reason"] = data["reason"]
query["oakey"] = (data["oakey"] && data["oakey"] != "N/A" ? data["oakey"] : data["akey"])
query["akey"] = data["akey"]
query["timestamp"] = banTimestamp
query["previous"] = (data["previous"] ? data["previous"] : 0)
query["chain"] = (data["chain"] ? data["chain"] : 0)
data = queryAPI("bans/add", query)
if (step == 2 || !centralConn)
var/list/ldata = data
if (!ldata) return "No data returned from query"
if (ldata["error"])
return ldata["error"]
//we can get away with just this because addBan only ever returns the details we passed in to begin with
var/list/row = ldata[ldata[1]]
var/client/adminC
var/client/targetC
for (var/client/C in clients) //grab clients if possible
if (C.ckey == row["akey"])
adminC = C
if (C.ckey == row["ckey"])
targetC = C
var/mob/targetM
if (!targetC)
for (var/mob/M in mobs) //Grab the mob if no target clients were found
if (M.ckey == row["ckey"])
targetM = M
if (text2num(row["chain"]) > 0) //Prepend our evasion attempt info for: the user, admins, notes (everything except the actual ban reason in the db)
row["reason"] = "\[Evasion Attempt x[row["chain"]]\] Previous Reason: [row["reason"]]"
var/replacement_text
if (targetC)
targetC.mob.unlock_medal("Banned", 1)
boutput(targetC, "<span style=\"color:red\"><BIG><B>You have been banned by [row["akey"]].<br>Reason: [row["reason"]]</B></BIG></span>")
boutput(targetC, "<span style=\"color:red\">To try to resolve this matter head to http://forum.ss13.co</span>")
else
replacement_text = "[row["ckey"]] (IP: [row["ip"]], CompID: [row["compID"]])"
if (!adminC)
adminC = (row["akey"] ? row["akey"] : "N/A")
var/expiry = getExpiry(text2num(row["timestamp"]))
if (expiry == 0)
if (targetC) boutput(targetC, "<span style=\"color:red\">This is a permanent ban.</span>")
logTheThing("admin", adminC, targetC, "has banned [targetC ? "%target%" : replacement_text]. Reason: [row["reason"]]. This is a permanent ban.")
logTheThing("diary", adminC, targetC, "has banned [targetC ? "%target%" : replacement_text]. Reason: [row["reason"]]. This is a permanent ban.", "admin")
var/adminMsg = "<span style=\"color:blue\">"
adminMsg += (istype(adminC, /client) ? key_name(adminC) : adminC)
adminMsg += " has banned [targetC ? targetC : replacement_text].<br>Reason: [row["reason"]]<br>This is a permanent ban.</span>"
message_admins(adminMsg)
else
if (targetC) boutput(targetC, "<span style=\"color:red\">This is a temporary ban, it will be removed in [expiry].</span>")
logTheThing("admin", adminC, targetC, "has banned [targetC ? "%target%" : replacement_text]. Reason: [row["reason"]]. This will be removed in [expiry].")
logTheThing("diary", adminC, targetC, "has banned [targetC ? "%target%" : replacement_text]. Reason: [row["reason"]]. This will be removed in [expiry].", "admin")
var/adminMsg = "<span style=\"color:blue\">"
adminMsg += (istype(adminC, /client) ? key_name(adminC) : adminC)
adminMsg += " has banned [targetC ? targetC : replacement_text].<br>Reason: [row["reason"]]<br>This will be removed in [expiry].</span>"
message_admins(adminMsg)
if (row["ckey"] && row["ckey"] != "N/A")
add_player_note("[row["ckey"]]", "[row["akey"]]", "Banned by [row["akey"]], reason \"[row["reason"]]\", duration: [(expiry == 0 ? "Permanent": "[expiry]")]")
var/ircmsg[] = new()
ircmsg["key"] = row["akey"]
ircmsg["key2"] = "[row["ckey"]] (IP: [row["ip"]], CompID: [row["compID"]])"
ircmsg["msg"] = row["reason"]
ircmsg["time"] = expiry
ircbot.export("ban", ircmsg)
if (targetC)
if (targetC.mob)
del(targetC.mob)
del(targetC)
if (targetM)
del(targetM)
return 0
//Starts the dialog for banning a dude
/client/proc/addBanDialog(target)
if (src.holder && usr.level >= LEVEL_SA)
var/mob/M
var/mobRef = 0
if (target && istype(target, /mob/))
mobRef = 1
M = target
if (mobRef)
if (M.client && M.client.holder && (M.client.holder.level >= usr.level))
alert("You can't ban another admin you huge jerk!!!!!")
return
var/data[] = new()
if (!mobRef)
data["ckey"] = input(usr, "Ckey (lowercase, only alphanumeric, no spaces, leave blank to skip)", "Ban") as null|text
data["compID"] = input(usr, "Computer ID (leave blank to skip)", "Ban") as null|text
data["ip"] = input(usr, "IP Address (leave blank to skip)", "Ban") as null|text
else
data["ckey"] = M.ckey
data["compID"] = M.computer_id
data["ip"] = M.lastKnownIP
if (!data["ckey"] && !data["ip"] && !data["compID"])
boutput(usr, "<span style=\"color:red\">You need to input a ckey or IP or computer ID, all cannot be blank.</span>")
return
boutput(usr, "<span style=\"color:red\"><b>You are currently banning the following player:</b></span>")
boutput(usr, "<b>Mob:</b> [mobRef ? M.name : "N/A"]")
boutput(usr, "<b>Key:</b> [data["ckey"] ? data["ckey"] : "N/A"] (IP: [data["ip"] ? data["ip"] : "N/A"], CompID: [data["compID"] ? data["compID"] : "N/A"])")
boutput(usr, "<span style=\"color:red\"><b>Make sure this is who you want to ban before continuing!</b></span>")
var/reason = input(usr,"Reason for ban?","Ban") as null|text
if(!reason)
boutput(usr, "<span style=\"color:red\">You need to enter a reason for the ban.</span>")
return
data["reason"] = reason
var/ban_time = input(usr,"How long will the ban be?","Ban") as null|anything in list("Half-hour","One Hour","Six Hours","One Day","Half a Week","One Week","One Month","Permanent","Custom")
var/mins = 0
switch(ban_time)
if("Half-hour")
mins = 30
if("One Hour")
mins = 60
if("Six Hours")
mins = 360
if("One Day")
mins = 1440
if("Half a Week")
mins = 5040
if("One Week")
mins = 10080
if("One Month")
mins = 43200
if("Permanent")
mins = "perma"
else
var/cust_mins = input(usr,"How many minutes? (1440 = one day)","BAN HE",1440) as null|num
if(!cust_mins)
boutput(usr, "<span style=\"color:red\">No time entered, cancelling ban.</span>")
return
if(cust_mins >= 525600)
boutput(usr, "<span style=\"color:red\">Ban time too long. Ban shortened to one year (525599 minutes).</span>")
mins = 525599
else
mins = cust_mins
data["mins"] = (mins == "perma" ? 0 : mins)
data["akey"] = usr.ckey
addBan(1, data)
else
alert("You need to be at least a Secondary Administrator to ban players.")
return 1
//Admin verb to add bans
/client/proc/cmd_admin_addban ()
set category = "Admin"
set name = "Add Ban"
set popup_menu = 0
addBanDialog()
return
/proc/editBan(step = 1, data)
set background = 1
if (step == 1)
var/banTimestamp = 0
if (data["mins"] > 0) //If a temp ban, calculate expiry
var/CMinutes = (world.realtime / 10) / 60
banTimestamp = (data["previous"] ? CMinutes + data["mins"] + 7200 : CMinutes + data["mins"]) //Add 5 days (7200 mins) onto the ban if it's an evasion attempt
var/query[] = new()
query["id"] = data["id"]
query["ckey"] = (data["ckey"] ? data["ckey"] : "N/A")
query["compID"] = (data["compID"] ? data["compID"] : "N/A")
query["ip"] = (data["ip"] ? data["ip"] : "N/A")
query["reason"] = data["reason"]
query["akey"] = data["akey"]
query["timestamp"] = banTimestamp
data = queryAPI("bans/edit", query)
if (step == 2 || !centralConn)
var/list/ldata = data
if (!ldata) return "No data returned from query"
if (ldata["error"])
return ldata["error"]
//we can get away with just this because editBan only ever acts on one ban (obviously)
var/list/row = ldata[ldata[1]]
var/client/adminC
for (var/client/C in clients) //grab us a good ol' admin client
if (C.ckey == row["akey"])
adminC = C
break
if (!adminC)
adminC = (row["akey"] ? row["akey"] : "N/A")
var/target = "[row["ckey"]] (IP: [row["ip"]], CompID: [row["compID"]])"
var/expiry = getExpiry(text2num(row["timestamp"]))
logTheThing("admin", adminC, target, "edited %target%'s ban. Reason: [row["reason"]] Duration: [(expiry == 0 ? "Permanent": "[expiry]")]")
logTheThing("diary", adminC, target, "edited %target%'s ban. Reason: [row["reason"]] Duration: [(expiry == 0 ? "Permanent": "[expiry]")]", "admin")
message_admins("<span style=\"color:blue\">[key_name(adminC)] edited [target]'s ban. Reason: [row["reason"]] Duration: [(expiry == 0 ? "Permanent": "[expiry]")]</span>")
var/ircmsg[] = new()
ircmsg["key"] = (istype(adminC, /client/) && adminC.key ? adminC.key : adminC)
ircmsg["name"] = (istype(adminC, /client/) && adminC.mob && adminC.mob.name ? adminC.mob.name : "N/A")
ircmsg["msg"] = "edited [target]'s ban. Reason: [row["reason"]]. Duration: [(expiry == 0 ? "Permanent": "[expiry]")]."
ircbot.export("admin", ircmsg)
return 0
/client/proc/editBanDialog(id, ckey, compID, ip, oreason, otimestamp)
if (src.holder && usr.level >= LEVEL_SA)
var/CMinutes = (world.realtime / 10) / 60
var/remaining = (text2num(otimestamp) - CMinutes)
if(!remaining || remaining < 0) remaining = 0
var/data[] = new()
data["ckey"] = input(usr, "Ckey (lowercase, only alphanumeric, no spaces)", "Ban", ckey) as null|text
data["compID"] = input(usr, "Computer ID (leave blank to skip)", "Ban", compID) as null|text
data["ip"] = input(usr, "IP Address", "Ban", ip) as null|text
if (!data["ckey"] && !data["ip"] && !data["compID"])
boutput(usr, "<span style=\"color:red\">You need to input a ckey or a compID or an IP, all cannot be blank.</span>")
return
var/reason = input(usr,"Reason for ban?","Ban", oreason) as null|text
if(!reason)
boutput(usr, "<span style=\"color:red\">You need to enter a reason for the ban.</span>")
return
data["reason"] = reason
var/ban_time = input(usr,"How long will the ban be? (select Custom to alter existing duration)","Ban") as null|anything in list("Half-hour","One Hour","Six Hours","One Day","Half a Week","One Week","One Month","Permanent","Custom")
var/mins = 0
switch(ban_time)
if("Half-hour")
mins = 30
if("One Hour")
mins = 60
if("Six Hours")
mins = 360
if("One Day")
mins = 1440
if("Half a Week")
mins = 5040
if("One Week")
mins = 10080
if("One Month")
mins = 43200
if("Permanent")
mins = "perma"
else
var/cust_mins = input(usr,"How many minutes? (1440 = one day)","BAN HE",remaining ? remaining : 1440) as null|num
if(!cust_mins)
boutput(usr, "<span style=\"color:red\">No time entered, cancelling ban.</span>")
return
if(cust_mins >= 525600)
boutput(usr, "<span style=\"color:red\">Ban time too long. Ban shortened to one year (525599 minutes).</span>")
mins = 525599
else
mins = cust_mins
data["id"] = id
data["reason"] = reason
data["mins"] = (mins == "perma" ? 0 : mins)
data["akey"] = usr.ckey
editBan(1, data)
src.holder.banPanel()
else
alert("You need to be at least a Secondary Administrator to ban players.")
return 1
/proc/deleteBan(step = 1, data)
set background = 1
if (step == 1)
var/query[] = new()
query["id"] = data["id"]
query["ckey"] = data["ckey"]
query["compID"] = data["compID"]
query["ip"] = data["ip"]
query["akey"] = data["akey"]
data = queryAPI("bans/delete", query)
if (step == 2 || !centralConn)
var/list/ldata = data
if (!ldata) return "No data returned from query"
if (ldata["error"])
return ldata["error"]
//we can get away with just this because deleteBan only ever acts on one ban (obviously)
var/list/row = ldata[ldata[1]]
var/client/adminC
for (var/client/C in clients)
if (C.ckey == row["akey"])
adminC = C
break
if (!adminC)
adminC = (row["akey"] ? row["akey"] : "N/A")
var/target = "[row["ckey"]] (IP: [row["ip"]], CompID: [row["compID"]])"
var/expired = (row["akey"] == "Auto Unbanner" ? 1 : 0)
if (expired)
logTheThing("admin", null, null, "[row["ckey"]]'s ban expired.")
logTheThing("diary", null, null, "[row["ckey"]]'s ban expired.", "admin")
message_admins("<span style=\"color:blue\">Ban expired for [target]</span>")
else
logTheThing("admin", adminC, null, "unbanned [row["ckey"]]")
logTheThing("diary", adminC, null, "unbanned [row["ckey"]]", "admin")
message_admins("<span style=\"color:blue\">[key_name(adminC)] unbanned [target]</span>")
var/ircmsg[] = new()
ircmsg["key"] = (istype(adminC, /client/) && adminC.key ? adminC.key : adminC)
ircmsg["name"] = (expired ? "\[Expired\]" : "[istype(adminC, /client/) && adminC.mob && adminC.mob.name ? adminC.mob.name : "N/A"]")
ircmsg["msg"] = (expired ? "[row["ckey"]]'s ban removed." : "deleted [row["ckey"]]'s ban.")
ircbot.export("admin", ircmsg)
return 0
/client/proc/deleteBanDialog(id, ckey, compID, ip, akey)
if (src.holder && usr.level >= LEVEL_SA)
if(alert(usr, "Are you sure you want to unban [ckey]?", "Confirmation", "Yes", "No") == "Yes")
var/data[] = new()
data["id"] = id
data["ckey"] = ckey
data["compID"] = compID
data["ip"] = ip
data["akey"] = akey
deleteBan(1, data)
src.holder.banPanel()
else
alert("You need to be at least a Secondary Administrator to remove bans.")
/*
/proc/addException(step = 1, data)
set background = 1
if (step == 1)
var/query[] = new()
query["ckey"] = data["ckey"]
query["akey"] = data["akey"]
data = queryAPI("bans/addException", query)
if (step == 2 || !centralConn)
var/list/ldata = data
if (!ldata) return "No data returned from query"
if (ldata["error"])
return ldata["error"]
//we can get away with just this because deleteBan only ever acts on one ban (obviously)
var/list/row = ldata[ldata[1]]
var/client/adminC
for (var/client/C in clients)
if (C.ckey == row["akey"])
adminC = C
break
if (!adminC)
adminC = (row["akey"] ? row["akey"] : "N/A")
var/target = "[row["ckey"]] (IP: [row["ip"]], CompID: [row["compID"]])"
var/expired = (row["akey"] == "Auto Unbanner" ? 1 : 0)
if (expired)
logTheThing("admin", null, null, "[row["ckey"]]'s ban expired.")
logTheThing("diary", null, null, "[row["ckey"]]'s ban expired.", "admin")
message_admins("<span style=\"color:blue\">Ban expired for [target]</span>")
else
logTheThing("admin", adminC, null, "unbanned [row["ckey"]]")
logTheThing("diary", adminC, null, "unbanned [row["ckey"]]", "admin")
message_admins("<span style=\"color:blue\">[key_name(adminC)] unbanned [target]</span>")
var/ircmsg[] = new()
ircmsg["key"] = (istype(adminC, /client/) && adminC.key ? adminC.key : adminC)
ircmsg["name"] = (expired ? "\[Expired\]" : "[istype(adminC, /client/) && adminC.mob && adminC.mob.name ? adminC.mob.name : "N/A"]")
ircmsg["msg"] = (expired ? "[row["ckey"]]'s ban removed." : "deleted [row["ckey"]]'s ban.")
ircbot.export("admin", ircmsg)
return 0
/client/proc/addExceptionDialog()
if (src.holder && usr.level >= LEVEL_SA)
if(alert(usr, "Are you sure you want to unban [ckey]?", "Confirmation", "Yes", "No") == "Yes")
var/data[] = new()
data["id"] = id
data["ckey"] = ckey
data["compID"] = compID
data["ip"] = ip
data["akey"] = akey
deleteBan(1, data)
src.holder.banPanel()
else
alert("You need to be at least a Secondary Administrator to add ban exceptions.")
*/
/////////////////////////
// BAN PANEL PROCS (called via ejhax)
/////////////////////////
/datum/admins/proc/banPanel()
var/CMinutes = (world.realtime / 10) / 60
var/bansHtml = grabResource("html/admin/banPanel.html")
var/windowName = "banPanel"
bansHtml = replacetext(bansHtml, "null /* window_name */", "\'[windowName]\'")
bansHtml = replacetext(bansHtml, "null /* ref_src */", "\'\ref[src]\'")
bansHtml = replacetext(bansHtml, "null /* cminutes */", "[CMinutes]")
if (centralConn)
bansHtml = replacetext(bansHtml, "null /* api_key */", "\'[md5(config.extserver_web_token)]\'")
usr << browse(bansHtml,"window=[windowName];size=1080x500")
/client/proc/openBanPanel()
set name = "Ban Panel"
set category = "Admin"
if (src.holder)
src.holder.banPanel()
return
//DEBUG (gets the latest ban and prints it out)
/proc/debugBans(step, data)
set background = 1
if (step == 1)
queryAPI("bans/debug")
if (step == 2)
var/list/ldata = data
for (var/e = 1, e <= ldata.len, e++) //each ban
var/id = ldata[e]
var/list/details = ldata[id]
boutput(world, id)
for (var/i = 1, i <= details.len, i++) //each item for this ban
boutput(world, "[details[i]]: [details[details[i]]]")
/////////////////////////
// LOCAL AND REMOTE DB SYNCHRONISATION
/////////////////////////
/proc/writeToBanLog(data)
if (!data) return 0
var/banLog = "data/banLog.log"
var/lastID = 0
if (fexists(banLog))
//Here we fetch the latest logID, increment it, then append our data as json
var/list/log = dd_file2list(banLog)
var/lastIndex = (log.len > 1 ? log.len - 1 : 1)
var/lastRow = log[lastIndex]
var/list/rowDetails = splittext(lastRow, ":")
lastID = text2num(rowDetails[1])
var/newID = lastID + 1
var/append = json_encode(data)
var/logFile = file(banLog)
boutput(logFile, "[newID]:[append]")
return 1
/proc/clearBanLog()
var banLog = "data/banLog.log"
if (fexists(banLog))
fdel(banLog)
return 1
/proc/forceUpdateLocalBans(latestLocalID, latestRemoteID)
//Construct the log IDs we need from the remote
var/needBans = ""
for (var/id = latestLocalID + 1, id <= latestRemoteID, id++)
needBans += "[id],"
needBans = copytext(needBans, 1, -1) //Remove the trailing comma
//Ask the remote for them
var/query[] = new()
query["ids"] = needBans
var/data[] = queryAPI("bans/updateLocal", query, 1)
if (!data) return 0
if (data["error"]) //Error returned from the API welp
logTheThing("debug", null, null, "<b>Bans Error</b>: Error returned in <b>forceUpdateLocalBans</b>: [data["error"]]")
logTheThing("diary", null, null, "Bans Error: Error returned in forceUpdateLocalBans: [data["error"]]", "debug")
return 0
logTheThing("debug", null, null, "UPDATE LOCAL DEBUG: data: [list2params(data)]")
//Loop through the bans we were given
for (var/row in data)
var/logID = row
if (!row) break
logTheThing("debug", null, null, "UPDATE LOCAL DEBUG: logID: [logID]")
var/list/details = json_decode(row[logID])
logTheThing("debug", null, null, "UPDATE LOCAL DEBUG: details: [list2params(details)]")
var/type = details["type"]
details.Remove(details["type"])
//Decide what to do with it
var/returnData[] = new()
if (type == "add")
returnData = addBanApiFallback(details)
if (type == "edit")
returnData = editBanApiFallback(details)
if (type == "delete")
returnData = deleteBanApiFallback(details)
if (returnData["error"]) //Error returned from the local db jeeeeez aint nothing going our way
logTheThing("debug", null, null, "<b>Local API Error</b> - Callback failed in <b>[type]BanApiFallback</b> with message: <b>[returnData["error"]]</b>")
logTheThing("diary", null, null, "<b>Local API Error</b> - Callback failed in [type]BanApiFallback with message: [returnData["error"]]", "debug")
if (returnData["showAdmins"])
message_admins("<span style=\"color:blue\"><b>Failed for route [type]BanApiFallback</b>: [returnData["error"]]</span>")
return 0
/*
for (var/e = 1, e <= data.len, e++) //each ban
var/logID = data[e]
logTheThing("debug", null, null, "UPDATE LOCAL DEBUG: logID: [logID]")
var/list/details = json_decode(data[logID])
logTheThing("debug", null, null, "UPDATE LOCAL DEBUG: details: [list2params(details)]")
var/type = details["type"]
details.Remove(details["type"])
//Decide what to do with it
var/returnData[] = new()
if (type == "add")
returnData = addBanApiFallback(details)
if (type == "edit")
returnData = editBanApiFallback(details)
if (type == "delete")
returnData = deleteBanApiFallback(details)
if (returnData["error"]) //Error returned from the local db jeeeeez aint nothing going our way
logTheThing("debug", null, null, "<b>Local API Error</b> - Callback failed in <b>[type]BanApiFallback</b> with message: <b>[returnData["error"]]</b>")
logTheThing("diary", null, null, "<b>Local API Error</b> - Callback failed in [type]BanApiFallback with message: [returnData["error"]]", "debug")
if (returnData["showAdmins"])
message_admins("<span style=\"color:blue\"><b>Failed for route [type]BanApiFallback</b>: [returnData[</span>"error"]]")
return 0
*/
return 1
/proc/forceUpdateRemoteBans(latestLocalID, latestRemoteID)
logTheThing("debug", null, null, "UPDATE REMOTE DEBUG: latestLocalID: [latestLocalID]")
logTheThing("debug", null, null, "UPDATE REMOTE DEBUG: latestRemoteID: [latestRemoteID]")
//Construct the log IDs the remote is missing
var/sendBans[] = new()
for (var/id = latestRemoteID + 1, id <= latestLocalID, id++)
sendBans.Add(id)
logTheThing("debug", null, null, "UPDATE REMOTE DEBUG: sendBans: [list2params(sendBans)]")
var/list/log = dd_file2list("data/banLog.log")
//Get the data for those log IDs
var/parsedLog[] = new()
for (var/i = 1, i <= log.len, i++)
var/row = log[i]
logTheThing("debug", null, null, "UPDATE REMOTE DEBUG: row: [row]")
var/list/rowDetails = splittext(row, ":")
logTheThing("debug", null, null, "UPDATE REMOTE DEBUG: rowDetails: [list2params(rowDetails)]")
var/logID = rowDetails[1]
logID = text2num(logID)
logTheThing("debug", null, null, "UPDATE REMOTE DEBUG: logID: [logID]")
if (logID in sendBans)
var/lengthToCut = length(logID)+3 //+1 for the colon, +2 for copytext being a shitass
var/details = copytext(row, lengthToCut)
logTheThing("debug", null, null, "UPDATE REMOTE DEBUG: details: [details]")
parsedLog["[logID]"] = details
logTheThing("debug", null, null, "UPDATE REMOTE DEBUG: parsedLog: [json_encode(parsedLog)]")
//Send it to the remote so it can update
var/query[] = new()
query["bans"] = json_encode(parsedLog)
queryAPI("bans/updateRemote", query)
return 1
//Universal proc for checking if both the local ban db and the remote are synced
/proc/bansParityCheck()
//Ok we should probably make sure this shit doesn't run on local servers
//yes we are checking filesize of the ban db here. it is a nice independent way of doing it
if (!centralConn || !fexists("data/localBans.db") || length(file("data/localBans.db")) < 102400)
return 0
//Get the latest logID from the local ban log
var/banLog = "data/banLog.log"
var/banLogF = file(banLog)
var/latestLocalID = 0
//Only operate on the log if it...exists and has stuff, naturally
if (fexists(banLog) && length(file2text(banLogF)) > 0)
var/list/log = dd_file2list(banLog)
var/lastIndex = (log.len > 1 ? log.len - 1 : 1)
var/lastRow = log[lastIndex]
log = splittext(lastRow, ":")
latestLocalID = log[1]
//Get the latest logID from the API
var/query[] = new()
query["latestLocalID"] = latestLocalID
var/data[] = queryAPI("bans/parity", query, 1)
if (!data) return 0
var/list/row = data[data[1]]
if (!row["logID"])
return "ERROR: logID is not present from remote, cannot compare parity."
var/latestRemoteID = row["logID"]
//Just making sure
latestLocalID = text2num(latestLocalID)
latestRemoteID = text2num(latestRemoteID)
//Local bans are out of date
if (latestLocalID < latestRemoteID)
if (forceUpdateLocalBans(latestLocalID, latestRemoteID))
clearBanLog() //we're synced! clean up
return "Updated local bans to match central server"
return
//Remote bans are out of date
if (latestRemoteID < latestLocalID)
if (forceUpdateRemoteBans(latestLocalID, latestRemoteID))
clearBanLog() //we're synced! clean up
return "Updated remote bans to match cached local changes"
return
//Nothing is out of date supposedly possibly maybe??
clearBanLog() //we're synced! clean up
return "Both databases are up to date. Yay!"
/proc/debugToggleCentralConn()
if (centralConn)
centralConn = 0
centralConnTries = 5
return "Set centralConn OFF"
else
centralConn = 1
centralConnTries = 0
return "Set centralConn ON"