Swapped out xban for xban v2

master
Vanessa Ezekowitz 2014-05-19 23:45:58 -04:00
parent 1850f3333c
commit 19f880b657
13 changed files with 374 additions and 458 deletions

View File

@ -1,25 +0,0 @@
Copyright (c) 2013, Diego Martínez
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,115 +0,0 @@
Extended Ban Mod for Minetest
-----------------------------
This mod registers all the IPs used by individual players, and can ban the
player when using any of them, even if he is not online at the moment.
License
-------
See file 'LICENSE.txt'.
Chat Commands
-------------
/xban <player> [<reason>]
Ban given player and all his IPs. If reason not given, it defaults to
"because random". If player is online at the moment, he/she is shown it. If
user is not online, it saves it in a list, and next time he connects from any
IP, or connects from a banned IP with different name, it gets banned again,
and new IP/username recorded.
/xtempban <player> <time> [<reason>]
Same as /xban, except it specifies a temporary ban.
<time> must be prefixed by a colon (':'), and can be in any of the
following formats:
123m - Ban for 123 minutes.
12h - Ban for 12 hours.
1d - Ban for 1 day.
1W - Ban for 1 week.
1M - Ban for 1 month (30 days).
If not specified, the ban is permanent and must be manually removed.
/xunban <player>
Unban given player and all his IPs.
Configuration
-------------
The following options can be used to change the behavior of `xban'. They
must be set in your server's `minetest.conf'.
xban.kick_guests = <bool>
Whether to kick "Guest" users (automatically generated player names).
Default is false.
xban.ban_message = <string>
The default ban message when not specified.
Default is "Because random.".
xban.guest_kick_message = <string>
Message sent before kicking guests. Not applicable if the option
`xban.kick_guests' is false.
Default is "Guest accounts are not allowed. Please choose a proper name.".
API
---
Other mods can make use of xban functionality if desired. You just need
to (opt)depend on xban.
xban.ban_player(name, time, reason)
Bans a given player <name> for <time> seconds with specified <reason>.
If <time> is nil, it acts as a permanent ban.
If <reason> is nil, the default reason "because random" is used.
xban.unban_player(name)
--> (true) or (nil, error)
Removes a player from the ban list.
Returns true on success, or nil plus error message on error.
xban.find_entry(name_or_ip)
--> (entry, index) or (nil)
Returns the database entry for the given player or IP. Please note that a
single entry is shared by several IPs and several user names at the same
time. See below for the format of entries. Also note that the table is not
copied, so modifications will be saved to disk.
xban.entries
This is the list of entries in the database. See below for format.
Files
-----
This mod only modifies a single file named 'players.iplist.v2' in the world
directory (and indirectly, 'ipban.txt'). The format is a serialized Lua table.
Each item in the table is in the following format:
{
names = {
["foo"] = true, -- To ban by name.
["123.45.67.89"] = true, -- To ban by IP.
...
},
banned = "Because random.", -- If nil, user is not banned.
expires = 123456, -- In time_t. If nil, user is permabanned.
privs = { ... }, -- Player privileges before the ban.
record = { -- Record of previous bans.
{
date = 123456, -- Date when the ban was issued.
time = 4123, -- Ban time. This is nil if permabanned.
reason = "asdf", -- Duh.
},
...
},
}
The old `players.iplist' DB format is still read at startup, and converted
to the new format. In this case, `banned' is set to the default ban reason,
and `expires' is unset.

View File

@ -1,94 +0,0 @@
-- Extended Ban Mod for Minetest
-- (C) 2013 Diego Martínez <kaeza>
-- See `LICENSE.txt' for details.
-- chat.lua: Chat commands.
minetest.register_chatcommand("xban", {
params = "<player> [<reason>]",
description = "Future ban all IPs for a given player",
privs = { ban=true, },
func = function(name, param)
param = param:trim()
local player_name, reason = param:match("([^ ]+)( *.*)")
if not player_name then
xban._.send(name, "Usage: /xban <player> [<reason>]")
return
end
reason = reason:trim()
xban._.ACTION("%s bans player '%s'. Reason: %s", name, player_name, reason)
if reason == "" then reason = nil end
local r, e = xban.ban_player(player_name, nil, reason)
if r then
xban._.send(name, "Success!")
else
xban._.send(name, "Error: %s", e)
end
end,
})
local mul = { [""]=1, s=1, m=60, h=60*60, d=60*60*24, W=60*60*24*7, M=60*60*24*30 }
local function parse_time(t)
local total = 0
for count, suffix in t:gmatch("(%d+)([mhdWM]?)") do
count = count and tonumber(count)
if count and suffix then
total = (total or 0) + (count * mul[suffix])
end
end
if total then return total end
end
minetest.register_chatcommand("xtempban", {
params = "<player> <time> [<reason>]",
description = "Future ban all IPs for a given player, temporarily",
privs = { ban=true, },
func = function(name, param)
param = param:trim()
local player_name, time, reason = param:match("([^ ]+) *([^ ]+)( *.*)")
if not (player_name and time) then
xban._.send(name, "Usage: /xtempban <player> <time> [<reason>]")
return
end
time = parse_time(time)
if not time then
xban._.send(name, "Invalid time format. Syntax is: [0-9]+[mhdWM]")
return
elseif time < 60 then
xban._.send(name, "Ban time must be at least 60 seconds.")
return
end
reason = reason:trim()
xban._.ACTION("%s bans player '%s' for %d seconds. Reason: %s",
name, player_name, time, reason
)
if reason == "" then reason = nil end
local r, e = xban.ban_player(player_name, time, reason)
if r then
xban._.send(name, "Success!")
else
xban._.send(name, "Error: %s", e)
end
end,
})
minetest.register_chatcommand("xunban", {
params = "<player>",
description = "Unban all IPs for a given player",
privs = { ban=true, },
func = function(name, param)
param = param:trim()
if param == "" then
xban._.send(name, "Usage: /xunban <player>")
return
end
local r, e = xban.unban_player(param)
if r then
xban._.send(name, "Success!")
else
xban._.send(name, "Error: %s", e)
end
end,
})

View File

@ -1,26 +0,0 @@
-- Extended Ban Mod for Minetest
-- (C) 2013 Diego Martínez <kaeza>
-- See `LICENSE.txt' for details.
-- conf.lua: Config routines.
xban.conf = { }
local conf = Settings(minetest.get_worldpath().."/xban.conf")
function xban.conf.get(k)
local v
v = conf:get(k)
if v and (v ~= "") then return v end
v = minetest.setting_get("xban."..k)
if v and (v ~= "") then return v end
end
function xban.conf.get_bool(k)
local v
v = conf:get(k)
if v and (v ~= "") then return conf:get_bool(k) end
v = minetest.setting_get("xban."..k)
if v and (v ~= "") then return minetest.setting_getbool("xban."..k) end
end

View File

@ -1,17 +0,0 @@
-- Extended Ban Mod for Minetest
-- (C) 2013 Diego Martínez <kaeza>
-- See `LICENSE.txt' for details.
-- init.lua: Initialization script.
xban = { }
xban._ = { } -- Internal functions.
local MP = minetest.get_modpath("xban")
dofile(MP.."/conf.lua")
dofile(MP.."/intr.lua")
dofile(MP.."/xban.lua")
dofile(MP.."/chat.lua")
dofile(MP.."/join.lua")

View File

@ -1,20 +0,0 @@
-- Extended Ban Mod for Minetest
-- (C) 2013 Diego Martínez <kaeza>
-- See `LICENSE.txt' for details.
-- intr.lua: Internal functions.
-- NOTE: Do not use these from other mods; they may change without notice.
local function mklogger(type)
return function(fmt, ...) minetest.log(type, "[xban] "..fmt:format(...)) end
end
xban._.INFO = mklogger("info")
xban._.WARN = mklogger("warning")
xban._.ACTION = mklogger("action")
function xban._.send(name, fmt, ...)
minetest.chat_send_player(name, "[xban] "..(fmt:format(...)))
end

View File

@ -1,37 +0,0 @@
-- Extended Ban Mod for Minetest
-- (C) 2013 Diego Martínez <kaeza>
-- See `LICENSE.txt' for details.
-- join.lua: On join player callback.
local KICK_GUESTS = xban.conf.get("kick_guests")
local DEF_GUEST_KICK_MESSAGE = (xban.conf.get("guest_kick_message")
or ("Guest accounts are not allowed in this server. "..
"Please choose a proper username and try again in a few minutes."
)
)
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
if (not name) or (name == "") then return end
local ip = minetest.get_player_ip(name)
if KICK_GUESTS and name:match("^Guest[0-9]+") then
minetest.after(1, xban.ban_player, name, 60*2, DEF_GUEST_KICK_MESSAGE)
return
end
local data = xban.find_entry(name, true)
if data.banned then
if (not data.expires)
or (os.time() <= data.expires) then
minetest.after(1, xban.ban_player, name, nil, data.banned)
return
end
end
data.names[name] = true
data.names[ip] = true
xban._.ACTION("%s: added new IP '%s' to list", name, ip)
xban.save_db()
end)

View File

@ -1,13 +0,0 @@
#
# Sample configuration file for xban mod.
#
# Whether guest accounts are disallowed.
#kick_guests = false
# Default ban message.
#ban_message = Because random.
# Default kick message for guests.
#guest_kick_message = Guest accounts are not allowed in this server. Please choose a proper username and try again in a few minutes.

View File

@ -1,111 +0,0 @@
-- Extended Ban Mod for Minetest
-- (C) 2013 Diego Martínez <kaeza>
-- See `LICENSE.txt' for details.
-- xban.lua: Core functions.
local DEF_BAN_MESSAGE = (xban.conf.get("ban_message")
or "Because random.")
local DB_FILE = minetest.get_worldpath().."/players.iplist.v2"
local DB_FILE_OLD = minetest.get_worldpath().."/players.iplist"
local unpack = unpack or table.unpack
local iplist = { }
local function get_ip(name)
if minetest.get_player_by_name(name) then
return minetest.get_player_ip(name)
end
end
function xban.find_entry(name_or_ip, create)
for index, data in ipairs(iplist) do
for k, v in pairs(data.names) do
if v and (k == name_or_ip) then
return data, index
end
end
end
if create then
local data = { names={} }
table.insert(iplist, data)
return data
end
end
local function load_db()
local f = io.open(DB_FILE, "r")
if not f then return end
xban._.INFO("Loading IP database...")
local db = minetest.deserialize(f:read("*a"))
f:close()
iplist = db
xban._.INFO("IP database loaded!")
end
function xban.save_db()
local f, e = io.open(DB_FILE, "w")
if not f then
xban._.WARN("Error saving IP database: %s", (e or ""))
return
end
xban._.INFO("Saving IP database...")
f:write(minetest.serialize(iplist))
f:close()
xban._.INFO("IP database saved!")
end
function xban.ban_player(name, time, reason)
local data = xban.find_entry(name, true)
local ip = get_ip(name)
data.names[name] = true
if ip then data.names[ip] = true end
reason = reason or DEF_BAN_REASON
data.banned = reason
if not data.record then data.record = { } end
table.insert(data.record, { date=os.time(), time=time, reason=reason })
data.privs = data.privs or minetest.get_player_privs(name)
minetest.after(1, function()
xban._.send(name,
"You have been banned from this server for the following reason: %s",
(reason or DEF_BAN_MESSAGE)
)
if time then
data.expires = os.time() + time
xban._.send(name, "Your ban will expire on %s.", os.date("%c", data.expires))
end
xban._.send(name, "Disconnection will follow shortly. Have a nice day :)")
end)
if minetest.auth_table[name] then
minetest.set_player_privs(name, { })
end
minetest.after(5, minetest.ban_player, name)
xban._.ACTION("Banned Names/IPs: %s", table.concat(data.names, ", "))
xban._.INFO("Revoked all privileges")
xban.save_db()
return true
end
function xban.unban_player(name)
local data = xban.find_entry(name)
if not data then return nil, "No such player." end
if data.privs and next(data.privs) then
minetest.set_player_privs(name, data.privs)
end
for _, nm in ipairs(data.names) do
minetest.unban_player_or_ip(nm)
end
data.banned = nil
xban.save_db()
return true
end
load_db()
xban.db = iplist

32
mods/xban2/doc/API.md Normal file
View File

@ -0,0 +1,32 @@
## Extended Ban Mod API
### ban_player
`xban.ban_player(player_or_ip, source, expires, reason)`
Ban a player and all of his/her alternative names and IPs.
#### Arguments:
* `player_or_ip` - Player to search for and ban. See note 1 below.
* `source` - Source of the ban. See note 2 below.
* `expires` - Time at which the ban expires. If nil, ban is permanent.
* `reason` - Reason for ban.
### unban_player
`xban.unban_player(player_or_ip, source)`
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.
### Notes
* 1: If player is currently online, all his accounts are kicked.
* 2: Mods using the xban API are advised to use the `"modname:source"`
format for `source` (for example: `"anticheat:main"`).

View File

@ -0,0 +1,45 @@
Database is a regular Lua script that returns a table.
Table has a single named field `timestamp' containing the time_t the
DB was last saved. It's not used in the mod and is only meant for
external use (I don't find filesystem timestamps too reliable).
Next is a simple array (number indices) of entries.
Each entry contains following fields:
[1] = {
-- Names/IPs associated with this entry
names = {
["foo"] = true,
["bar"] = true,
["123.45.67.89"] = true,
},
banned = true, -- Whether this user is banned
-- Other fields do not apply if false
time = 12341234, -- Time of last ban (*1)
expires = 43214321 -- Time at which ban expires (*2)
-- If nil, permanent ban
reason = "asdf", -- Reason for ban
source = "qwerty", -- Source of ban (*2)
record = {
[1] = {
source = "asdf",
reason = "qwerty",
time = 12341234,
expires = 43214321,
},
[1] = {
source = "asdf",
reason = "Unbanned", -- When unbanned
time = 12341234,
},
},
}
Notes:
(*1) All times are expressed in whatever unit `os.time()' uses
(`time_t' on most (all?) systems).
(*2) Mods using the xban API are advised to use the "modname:source"
format for `source' (for example: "anticheat:main").

266
mods/xban2/init.lua Normal file
View File

@ -0,0 +1,266 @@
xban = { }
local MP = minetest.get_modpath(minetest.get_current_modname())
dofile(MP.."/serialize.lua")
local db = { }
local tempbans = { }
local DEF_SAVE_INTERVAL = 300 -- 5 minutes
local DEF_DB_FILENAME = minetest.get_worldpath().."/xban.db"
local DB_FILENAME = minetest.setting_get("xban.db_filename")
local SAVE_INTERVAL = tonumber(
minetest.setting_get("xban.db_save_interval")) or DEF_SAVE_INTERVAL
if (not DB_FILENAME) or (DB_FILENAME == "") then
DB_FILENAME = DEF_DB_FILENAME
end
local function make_logger(level)
return function(text, ...)
minetest.log(level, "[xban] "..text:format(...))
end
end
local ACTION = make_logger("action")
local INFO = make_logger("info")
local WARNING = make_logger("warning")
local ERROR = make_logger("error")
local unit_to_secs = {
s = 1, m = 60, h = 3600,
D = 86400, W = 604800, M = 2592000, Y = 31104000,
[""] = 1,
}
local function parse_time(t) --> secs
local secs = 0
for num, unit in t:gmatch("(%d+)([smhDWMY]?)") do
secs = secs + (tonumber(num) * (unit_to_secs[unit] or 1))
end
return secs
end
function xban.find_entry(player, create) --> entry, index
for index, e in ipairs(db) do
for name in pairs(e.names) do
if name == player then
return e, index
end
end
end
if create then
local e = {
names = { [player]=true },
banned = false,
record = { },
}
table.insert(db, e)
return e, #db
end
return nil
end
function xban.get_info(player) --> ip_name_list, banned, last_record
local e = xban.find_entry(player)
if not e then
return nil, "No such entry"
end
return e.names, e.banned, e.record[#e.record]
end
function xban.ban_player(player, source, expires, reason) --> bool, err
local e = xban.find_entry(player, true)
local rec = {
source = source,
time = os.time(),
expires = expires,
reason = reason,
}
table.insert(e.record, rec)
e.names[player] = true
local pl = minetest.get_player_by_name(player)
if pl then
local ip = minetest.get_player_ip(player)
if ip then
e.names[ip] = true
end
end
e.reason = reason
e.time = rec.time
e.expires = expires
e.banned = true
local msg
local date = (expires and os.date("%c", expires)
or "the end of time")
if expires then
table.insert(tempbans, e)
msg = ("Banned: Expires: %s, Reason: %s"):format(date, reason)
else
msg = ("Banned: Reason: %s"):format(reason)
end
for nm in pairs(e.names) do
minetest.kick_player(nm, msg)
end
ACTION("%s bans %s until %s for reason: %s", source, player,
date, reason)
ACTION("Banned Names/IPs: %s", table.concat(e.names, ", "))
return true
end
function xban.unban_player(player, source) --> bool, err
local e = xban.find_entry(player)
if not e then
return nil, "No such entry"
end
local rec = {
source = source,
time = os.time(),
reason = "Unbanned",
}
table.insert(e.record, rec)
e.banned = false
e.reason = nil
e.expires = nil
e.time = nil
ACTION("%s unbans %s", source, player)
ACTION("Unbanned Names/IPs: %s", table.concat(e.names, ", "))
return true
end
minetest.register_on_prejoinplayer(function(name, ip)
local e = xban.find_entry(name) or xban.find_entry(ip, true)
e.names[name] = true
e.names[ip] = true
if e.banned then
local date = (e.expires and os.date("%c", e.expires)
or "the end of time")
return ("Banned: Expires: %s, Reason: %s"):format(
date, e.reason)
end
end)
minetest.register_chatcommand("xban", {
description = "XBan a player",
params = "<player> <reason>",
privs = { ban=true },
func = function(name, params)
local plname, reason = params:match("(%S+)%s+(.+)")
if not (plname and reason) then
minetest.chat_send_player(name,
"Usage: /xban <player> <reason>")
return
end
xban.ban_player(plname, name, nil, reason)
minetest.chat_send_player(name,
("Banned %s."):format(plname))
end,
})
minetest.register_chatcommand("xtempban", {
description = "XBan a player temporarily",
params = "<player> <time> <reason>",
privs = { ban=true },
func = function(name, params)
local plname, time, reason = params:match("(%S+)%s+(%S+)%s+(.+)")
if not (plname and time and reason) then
minetest.chat_send_player(name,
"Usage: /xtempban <player> <time> <reason>")
return
end
time = parse_time(time)
if time < 60 then
minetest.chat_send_player(name,
"You must ban for at least 60 seconds.")
return
end
local expires = os.time() + time
xban.ban_player(plname, name, expires, reason)
minetest.chat_send_player(name,
("Banned %s until %s."):format(
plname, os.date("%c", expires)))
end,
})
minetest.register_chatcommand("xunban", {
description = "XUnBan a player",
params = "<player_or_ip>",
privs = { ban=true },
func = function(name, params)
local plname = params:match("%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)
minetest.chat_send_player(name,
("Unbanned %s."):format(plname))
end,
})
local function check_temp_bans()
minetest.after(60, check_temp_bans)
local to_rm = { }
local now = os.time()
for i, e in ipairs(tempbans) do
if e.expires and (e.expires <= now) then
table.insert(to_rm, i)
e.banned = false
e.expires = nil
e.reason = nil
e.time = nil
end
end
for _, i in ipairs(to_rm) do
table.remove(tempbans, i)
end
end
local function save_db()
minetest.after(SAVE_INTERVAL, save_db)
local ok
local f, e = io.open(DB_FILENAME, "wt")
db.timestamp = os.time()
if f then
ok, e = f:write(xban.serialize(db))
WARNING("Unable to save database: %s", e)
end
if f then f:close() end
return
end
local function load_db()
local f, e = io.open(DB_FILENAME, "rt")
if not f then
WARNING("Unable to load database: %s", e)
return
end
local cont
cont, e = f:read("*a")
if not cont then
WARNING("Unable to load database: %s", e)
return
end
local t = minetest.deserialize(cont)
if not t then
WARNING("Unable to load database: %s",
"Deserialization failed")
return
end
db = t
tempbans = { }
for _, entry in ipairs(db) do
if entry.banned and entry.expires then
table.insert(tempbans, entry)
end
end
end
minetest.register_on_shutdown(save_db)
minetest.after(SAVE_INTERVAL, save_db)
load_db()
xban.db = db

31
mods/xban2/serialize.lua Normal file
View File

@ -0,0 +1,31 @@
local function repr(x)
if type(x) == "string" then
return ("%q"):format(x)
else
return tostring(x)
end
end
local function my_serialize_2(t, level)
level = level or 0
local lines = { }
local indent = ("\t"):rep(level)
for k, v in pairs(t) do
local typ = type(v)
if typ == "table" then
table.insert(lines,
indent..("[%s] = {\n"):format(repr(k))
..my_serialize_2(v, level + 1).."\n"
..indent.."},")
else
table.insert(lines,
indent..("[%s] = %s,"):format(repr(k), repr(v)))
end
end
return table.concat(lines, "\n")
end
function xban.serialize(t)
return "return {\n"..my_serialize_2(t, 1).."\n}"
end