aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md16
-rw-r--r--doc/API.md37
-rw-r--r--doc/dbformat.txt2
-rw-r--r--gui.lua2
-rw-r--r--init.lua169
5 files changed, 200 insertions, 26 deletions
diff --git a/README.md b/README.md
index 75f8e88..a3b75c1 100644
--- a/README.md
+++ b/README.md
@@ -47,9 +47,21 @@ up. For example, `1Y3M3D7h` will ban for 1 year, 3 months, 3 days and 7 hours.
Unbans a player.
-**Usage:** `/xunban <player_or_ip>`
+**Usage:** `/xunban <player_or_ip> <reason>`
-**Example:** `/xunban Joe`
+**Example:** `/xunban Joe has promised not to grief again`
+
+### `xnote`
+
+Adds a note to a player's record
+
+**Usage:** `/xnote <player_or_ip> <note>`
+
+**Example:** `/xunban Joe has behaved suspiciously
+
+### `xkick`
+
+Same as `xban`, only that it kicks the player instead of banning.`
### `xban_record`
diff --git a/doc/API.md b/doc/API.md
index bee7c42..2353cb4 100644
--- a/doc/API.md
+++ b/doc/API.md
@@ -16,14 +16,47 @@ Ban a player and all of his/her alternative names and IPs.
### unban_player
-`xban.unban_player(player_or_ip, source)`
+`xban.unban_player(player_or_ip, source, reason)`
+
+Unban a player and all of his/her alternative names and IPs. A reason
+may be given for this unbanning, so other moderators can follow your
+thoughts.
-Unban a player and all of his/her alternative names and IPs.
#### Arguments:
* `player_or_ip` - Player to search for and unban.
* `source` - Source of the ban. See note 2 below.
+* `reason` - Reason for unbanning.
+
+### add_record:
+
+`xban.add_record(player, record)`
+
+Adds a record to the player's criminal record.
+
+#### Arguments:
+
+* `player` - Name of a player
+* `record` - a xban record (see below)
+
+### Player record format:
+
+
+ local record = {
+ source = "admin",
+ time = os.time(),
+ expires = nil,
+ reason = "bad behaviour",
+ type = "ban",
+ }
+
+* `source` - who issued the record
+* `time` - time at which the record was issued
+* `expires` - time at which the record expires, `nil` for never
+* `reason` - reason for record
+* `type` - type of record.
+
### Notes
diff --git a/doc/dbformat.txt b/doc/dbformat.txt
index 71b25a5..a16737d 100644
--- a/doc/dbformat.txt
+++ b/doc/dbformat.txt
@@ -29,11 +29,13 @@ Each entry contains following fields:
reason = "qwerty",
time = 12341234,
expires = 43214321,
+ type = "ban"
},
[1] = {
source = "asdf",
reason = "Unbanned", -- When unbanned
time = 12341234,
+ type = "unban"
},
},
}
diff --git a/gui.lua b/gui.lua
index 66fc89a..99ae6ec 100644
--- a/gui.lua
+++ b/gui.lua
@@ -43,7 +43,7 @@ local function get_record_simple(name)
end
local record = { }
for _, rec in ipairs(e.record) do
- local msg = (os.date("%Y-%m-%d %H:%M:%S", rec.time).." | "
+ local msg = (os.date("%Y-%m-%d %H:%M:%S", rec.time).." | " .. (rec.type or "ban").." | "
..(rec.reason or "No reason given."))
table.insert(record, msg)
end
diff --git a/init.lua b/init.lua
index 68a3508..6cf4691 100644
--- a/init.lua
+++ b/init.lua
@@ -1,4 +1,3 @@
-
xban = { MP = minetest.get_modpath(minetest.get_current_modname()) }
dofile(xban.MP.."/serialize.lua")
@@ -8,6 +7,8 @@ local tempbans = { }
local DEF_SAVE_INTERVAL = 300 -- 5 minutes
local DEF_DB_FILENAME = minetest.get_worldpath().."/xban.db"
+local CLEAN_IP_SECONDS = 24*60*60*7 -- time after which innocent player IPs should get removed
+local CLEAN_INTERVAL = 3600 -- interval at which the db should be purged of old IPs
local DB_FILENAME = minetest.settings:get("xban.db_filename")
local SAVE_INTERVAL = tonumber(
@@ -40,6 +41,11 @@ local function parse_time(t) --> secs
return secs
end
+function xban.is_ip(name)
+ -- checks if name is an ipv4 or ipv6 address
+ return string.match(name, "%.") or string.match(name, "%:")
+end
+
function xban.find_entry(player, create) --> entry, index
for index, e in ipairs(db) do
for name in pairs(e.names) do
@@ -69,6 +75,23 @@ function xban.get_info(player) --> ip_name_list, banned, last_record
return e.names, e.banned, e.record[#e.record]
end
+function xban.add_record(player, record)
+ -- Add records for other punishments banned.
+ local e = xban.find_entry(player, true)
+ table.insert(e.record, record)
+end
+
+function xban.add_property(player, property, value)
+ -- adds a property to a player, for instance a "jailed" property which indicates that a player is jailed
+ local e = xban.find_entry(player, true)
+ e[property] = value
+end
+function xban.get_property(player, property)
+ local e = xban.find_entry(player, true)
+ return e[property]
+end
+
+
function xban.ban_player(player, source, expires, reason) --> bool, err
if xban.get_whitelist(player) then
return nil, "Player is whitelisted; remove from whitelist first"
@@ -82,6 +105,7 @@ function xban.ban_player(player, source, expires, reason) --> bool, err
time = os.time(),
expires = expires,
reason = reason,
+ type = "ban",
}
table.insert(e.record, rec)
e.names[player] = true
@@ -89,7 +113,7 @@ function xban.ban_player(player, source, expires, reason) --> bool, err
if pl then
local ip = minetest.get_player_ip(player)
if ip then
- e.names[ip] = true
+ e.names[ip] = os.time()
end
e.last_pos = pl:getpos()
end
@@ -115,7 +139,7 @@ function xban.ban_player(player, source, expires, reason) --> bool, err
return true
end
-function xban.unban_player(player, source) --> bool, err
+function xban.unban_player(player, source, reason) --> bool, err
local e = xban.find_entry(player)
if not e then
return nil, "No such entry"
@@ -123,7 +147,8 @@ function xban.unban_player(player, source) --> bool, err
local rec = {
source = source,
time = os.time(),
- reason = "Unbanned",
+ reason = (reason or ""),
+ type = "unban"
}
table.insert(e.record, rec)
e.banned = false
@@ -156,6 +181,24 @@ function xban.add_whitelist(name_or_ip, source)
}
return true
end
+function xban.get_account_names(e)
+ -- get accounts associated with entry
+ local names = {}
+ if not e then
+ return nil, ("No entry for `%s'"):format(player)
+ end
+ for name in pairs(e.names) do
+ if not xban.is_ip(name) and name ~= player then
+ table.insert(names, name)
+ end
+ end
+ return names
+end
+
+function xban.get_alt_accounts(player)
+ local e = xban.find_entry(player)
+ return xban.get_account_names(e)
+end
function xban.get_record(player)
local e = xban.find_entry(player)
@@ -166,20 +209,22 @@ function xban.get_record(player)
end
local record = { }
for _, rec in ipairs(e.record) do
- local msg = rec.reason or "No reason given."
+ local msg = rec.type or "ban"
+ msg = msg .. ": " .. rec.reason or "No reason given."
if rec.expires then
- msg = msg..(", Expires: %s"):format(os.date("%c", e.expires))
+ msg = msg..(", Expires: %s"):format(os.date("%Y-%m-%d %H:%M:%S", rec.expires))
end
if rec.source then
msg = msg..", Source: "..rec.source
end
- table.insert(record, ("[%s]: %s"):format(os.date("%c", e.time), msg))
+ table.insert(record, ("[%s]: %s"):format(os.date("%Y-%m-%d %H:%M:%S", rec.time), msg))
end
local last_pos
if e.last_pos then
last_pos = ("User was last seen at %s"):format(
minetest.pos_to_string(e.last_pos))
end
+
return record, last_pos
end
@@ -209,7 +254,7 @@ minetest.register_on_joinplayer(function(player)
end
e.names[name] = true
if ip then
- e.names[ip] = true
+ e.names[ip] = os.time()
end
e.last_seen = os.time()
end)
@@ -248,18 +293,63 @@ minetest.register_chatcommand("xtempban", {
end,
})
+minetest.register_chatcommand("xnote", {
+ description = "Add a note to a player's criminal record",
+ params = "<player> <note>",
+ privs = { kick=true },
+ func = function(name, params)
+ local plname, note = params:match("(%S+)%s+(.+)")
+ if not (plname and note) then
+ return false, "Usage: /xnote <player> <note>"
+ end
+ local record = {
+ source = name,
+ time = os.time(),
+ expires = nil,
+ reason = note,
+ type = "note",
+ }
+ xban.add_record(plname, record)
+ return true, ("Added note for %s."):format(plname)
+ end,
+})
+
+minetest.register_chatcommand("xkick", {
+ description = "Kicks a player",
+ params = "<player> <reason>",
+ privs = { kick=true },
+ func = function(name, params)
+ local plname, note = params:match("(%S+)%s+(.+)")
+ if not (plname and note) then
+ return false, "Usage: /xkick <player> <reason>"
+ end
+ local record = {
+ source = name,
+ time = os.time(),
+ expires = nil,
+ reason = note,
+ type = "kick",
+ }
+ xban.add_record(plname, record)
+ minetest.kick_player(plname)
+ return true, ("Kicked %s."):format(plname)
+ end,
+})
+
+
+
minetest.register_chatcommand("xunban", {
description = "XUnBan a player",
- params = "<player_or_ip>",
+ params = "<player_or_ip> <reason>",
privs = { ban=true },
func = function(name, params)
- local plname = params:match("%S+")
+ local plname, reason = params:match("(%S+)%s+(.+)")
if not plname then
minetest.chat_send_player(name,
"Usage: /xunban <player_or_ip>")
return
end
- local ok, e = xban.unban_player(plname, name)
+ local ok, e = xban.unban_player(plname, name, reason)
return ok, ok and ("Unbanned %s."):format(plname) or e
end,
})
@@ -267,23 +357,31 @@ minetest.register_chatcommand("xunban", {
minetest.register_chatcommand("xban_record", {
description = "Show the ban records of a player",
params = "<player_or_ip>",
- privs = { ban=true },
+ privs = { kick=true },
func = function(name, params)
local plname = params:match("%S+")
if not plname then
return false, "Usage: /xban_record <player_or_ip>"
end
local record, last_pos = xban.get_record(plname)
+ local alt_accounts = xban.get_alt_accounts(plname)
+ local msg
+ if alt_accounts then
+ msg = "Alt accounts: " .. table.concat(alt_accounts, ", ")
+ end
if not record then
local err = last_pos
minetest.chat_send_player(name, "[xban] "..err)
- return
- end
- for _, e in ipairs(record) do
- minetest.chat_send_player(name, "[xban] "..e)
+ else
+ for _, e in ipairs(record) do
+ minetest.chat_send_player(name, "[xban] "..e)
+ end
+ if last_pos then
+ minetest.chat_send_player(name, "[xban] "..last_pos)
+ end
end
- if last_pos then
- minetest.chat_send_player(name, "[xban] "..last_pos)
+ if msg then
+ minetest.chat_send_player(name, "[xban] "..msg)
end
return true, "Record listed."
end,
@@ -315,10 +413,34 @@ minetest.register_chatcommand("xban_wl", {
})
+local function clean_db()
+ -- Removes old IP addresses for data protection and false positive
+ -- prevention
+ local cutoff = os.time() - CLEAN_IP_SECONDS
+ local cleaned = 0
+ for _,entry in ipairs(db) do
+ if not entry.banned then
+ -- only remove innocent player's ip addresses, rest can be
+ -- kept to ensure server security
+ for name, time in pairs(entry.names) do
+ if xban.is_ip(name) and (time == true or time < cutoff ) then
+ entry.names[name] = nil
+ cleaned = cleaned + 1
+ end
+ end
+ end
+ end
+ ACTION("Cleaned %d old IP addresses.", cleaned)
+end
+
local function check_temp_bans()
minetest.after(60, check_temp_bans)
local to_rm = { }
local now = os.time()
+ if not db.nextclean or db.nextclean < now then
+ clean_db()
+ db.nextclean = now + CLEAN_INTERVAL
+ end
for i, e in ipairs(tempbans) do
if e.expires and (e.expires <= now) then
table.insert(to_rm, i)
@@ -333,6 +455,7 @@ local function check_temp_bans()
end
end
+
local function save_db()
minetest.after(SAVE_INTERVAL, save_db)
local f, e = io.open(DB_FILENAME, "wt")
@@ -375,15 +498,20 @@ local function load_db()
end
end
+local function has_alt_accounts (e)
+ local a = xban.get_account_names(e)
+ return a and #a > 1
+end
+
minetest.register_chatcommand("xban_cleanup", {
- description = "Removes all non-banned entries from the xban db",
+ description = "Removes all uninteresting entries from the xban db",
privs = { server=true },
func = function(name, params)
local old_count = #db
local i = 1
while i <= #db do
- if not db[i].banned then
+ if #db[i].record == 0 and not has_alt_accounts(db[i]) then
-- not banned, remove from db
table.remove(db, i)
else
@@ -394,7 +522,6 @@ minetest.register_chatcommand("xban_cleanup", {
-- save immediately
save_db()
-
return true, "Removed " .. (old_count - #db) .. " entries, new db entry-count: " .. #db
end,
})