Reimplement authentication handler in Lua; now we have 1) infinite privilege names, 2) minetest.register_authentication_handler()

This commit is contained in:
Perttu Ahola 2012-03-30 18:42:18 +03:00
parent ed1ff06867
commit 7cad0a2dcd
11 changed files with 477 additions and 727 deletions

@ -12,7 +12,7 @@
print = minetest.debug
--
--
-- Define some random basic things
--
function basic_dump2(o)
@ -89,6 +89,19 @@ function dump(o, dumped)
end
end
function string:split(sep)
local sep, fields = sep or ",", {}
local pattern = string.format("([^%s]+)", sep)
self:gsub(pattern, function(c) fields[#fields+1] = c end)
return fields
end
function string:trim()
return (self:gsub("^%s*(.-)%s*$", "%1"))
end
assert(string.trim("\n \t\tfoo\t ") == "foo")
--
-- Item definition helpers
--
@ -848,6 +861,21 @@ function minetest.get_connected_players()
return list
end
minetest.registered_privileges = {}
function minetest.register_privilege(name, description)
minetest.registered_privileges[name] = description
end
minetest.register_privilege("interact", "Can interact with things and modify the world")
minetest.register_privilege("teleport", "Can use /teleport command")
minetest.register_privilege("settime", "Can use /time")
minetest.register_privilege("privs", "Can modify privileges")
minetest.register_privilege("server", "Can do server maintenance stuff")
minetest.register_privilege("shout", "Can speak in chat")
minetest.register_privilege("ban", "Can ban and unban players")
minetest.register_privilege("give", "Can use /give and /giveme")
minetest.register_privilege("password", "Can use /setpassword and /clearpassword")
--
-- Chat commands
--
@ -873,7 +901,7 @@ minetest.register_chatcommand("help", {
if def.description and def.description ~= "" then msg = msg .. ": " .. def.description end
return msg
end
if not param or param == "" then
if param == "" then
local msg = ""
cmds = {}
for cmd, def in pairs(minetest.chatcommands) do
@ -905,18 +933,89 @@ minetest.register_chatcommand("help", {
-- Register C++ commands without functions
minetest.register_chatcommand("me", {params = nil, description = "chat action (eg. /me orders a pizza)"})
minetest.register_chatcommand("status", {description = "print server status line"})
minetest.register_chatcommand("privs", {params = "<name>", description = "print out privileges of player"})
minetest.register_chatcommand("shutdown", {params = "", description = "shutdown server", privs = {server=true}})
minetest.register_chatcommand("setting", {params = "<name> = <value>", description = "set line in configuration file", privs = {server=true}})
minetest.register_chatcommand("clearobjects", {params = "", description = "clear all objects in world", privs = {server=true}})
minetest.register_chatcommand("time", {params = "<0...24000>", description = "set time of day", privs = {settime=true}})
minetest.register_chatcommand("teleport", {params = "<X>,<Y>,<Z>", description = "teleport to given position", privs = {teleport=true}})
minetest.register_chatcommand("grant", {params = "<name> <privilege>", description = "Give privilege to player", privs = {privs=true}})
minetest.register_chatcommand("revoke", {params = "<name> <privilege>", description = "Remove privilege from player", privs = {privs=true}})
minetest.register_chatcommand("ban", {params = "<name>", description = "ban IP of player", privs = {ban=true}})
minetest.register_chatcommand("unban", {params = "<name/ip>", description = "remove IP ban", privs = {ban=true}})
minetest.register_chatcommand("setpassword", {params = "<name> <password>", description = "set given password", privs = {password=true}})
minetest.register_chatcommand("clearpassword", {params = "<name>", description = "set empty password", privs = {password=true}})
-- Register some other commands
minetest.register_chatcommand("privs", {
params = "<name>",
description = "print out privileges of player",
func = function(name, param)
if param == "" then
param = name
else
if not minetest.check_player_privs(name, {privs=true}) then
minetest.chat_send_player(name, "Privileges of "..param.." are hidden from you.")
end
end
privs = {}
for priv, _ in pairs(minetest.get_player_privs(param)) do
table.insert(privs, priv)
end
minetest.chat_send_player(name, "Privileges of "..param..": "..table.concat(privs, " "))
end,
})
minetest.register_chatcommand("grant", {
params = "<name> <privilege>",
description = "Give privilege to player",
privs = {privs=true},
func = function(name, param)
local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)")
if not grantname or not grantprivstr then
minetest.chat_send_player(name, "Invalid parameters (see /help grant)")
return
end
local grantprivs = minetest.string_to_privs(grantprivstr)
local privs = minetest.get_player_privs(grantname)
for priv, _ in pairs(grantprivs) do
privs[priv] = true
end
minetest.set_player_privs(grantname, privs)
end,
})
minetest.register_chatcommand("revoke", {
params = "<name> <privilege>",
description = "Remove privilege from player",
privs = {privs=true},
func = function(name, param)
local revokename, revokeprivstr = string.match(param, "([^ ]+) (.+)")
if not revokename or not revokeprivstr then
minetest.chat_send_player(name, "Invalid parameters (see /help revoke)")
return
end
local revokeprivs = minetest.string_to_privs(revokeprivstr)
local privs = minetest.get_player_privs(revokename)
for priv, _ in pairs(revokeprivs) do
table.remove(privs, priv)
end
minetest.set_player_privs(revokename, privs)
end,
})
minetest.register_chatcommand("setpassword", {
params = "<name> <password>",
description = "set given password",
privs = {password=true},
func = function(name, param)
if param == "" then
minetest.chat_send_player(name, "Password field required")
return
end
minetest.set_player_password(name, param)
end,
})
minetest.register_chatcommand("clearpassword", {
params = "<name>",
description = "set empty password",
privs = {password=true},
func = function(name, param)
minetest.set_player_password(name, '')
end,
})
--
-- Builtin chat handler
@ -924,6 +1023,9 @@ minetest.register_chatcommand("clearpassword", {params = "<name>", description =
minetest.register_on_chat_message(function(name, message)
local cmd, param = string.match(message, "/([^ ]+) *(.*)")
if not param then
param = ""
end
local cmd_def = minetest.chatcommands[cmd]
if cmd_def then
if not cmd_def.func then
@ -942,6 +1044,149 @@ minetest.register_on_chat_message(function(name, message)
return false
end)
--
-- Authentication handler
--
function minetest.string_to_privs(str)
assert(type(str) == "string")
privs = {}
for _, priv in pairs(string.split(str, ',')) do
privs[priv:trim()] = true
end
return privs
end
function minetest.privs_to_string(privs)
assert(type(privs) == "table")
list = {}
for priv, bool in pairs(privs) do
if bool then
table.insert(list, priv)
end
end
return table.concat(list, ',')
end
assert(minetest.string_to_privs("a,b").b == true)
assert(minetest.privs_to_string({a=true,b=true}) == "a,b")
minetest.auth_file_path = minetest.get_worldpath().."/auth.txt"
minetest.auth_table = {}
local function read_auth_file()
local newtable = {}
local file, errmsg = io.open(minetest.auth_file_path, 'rb')
if not file then
error(minetest.auth_file_path.." could not be opened for reading: "..errmsg)
end
for line in file:lines() do
if line ~= "" then
local name, password, privilegestring = string.match(line, "([^:]*):([^:]*):([^:]*)")
if not name or not password or not privilegestring then
error("Invalid line in auth.txt: "..dump(line))
end
local privileges = minetest.string_to_privs(privilegestring)
newtable[name] = {password=password, privileges=privileges}
end
end
io.close(file)
minetest.auth_table = newtable
end
local function save_auth_file()
local newtable = {}
-- Check table for validness before attempting to save
for name, stuff in pairs(minetest.auth_table) do
assert(type(name) == "string")
assert(name ~= "")
assert(type(stuff) == "table")
assert(type(stuff.password) == "string")
assert(type(stuff.privileges) == "table")
end
local file, errmsg = io.open(minetest.auth_file_path, 'w+b')
if not file then
error(minetest.auth_file_path.." could not be opened for writing: "..errmsg)
end
for name, stuff in pairs(minetest.auth_table) do
local privstring = minetest.privs_to_string(stuff.privileges)
file:write(name..":"..stuff.password..":"..privstring..'\n')
end
io.close(file)
end
read_auth_file()
minetest.builtin_auth_handler = {
get_auth = function(name)
assert(type(name) == "string")
if not minetest.auth_table[name] then
minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
end
if minetest.is_singleplayer() or name == minetest.setting_get("name") then
return {
password = "",
privileges = minetest.registered_privileges
}
else
return minetest.auth_table[name]
end
end,
create_auth = function(name, password)
assert(type(name) == "string")
assert(type(password) == "string")
minetest.log('info', "Built-in authentication handler adding player '"..name.."'")
minetest.auth_table[name] = {
password = password,
privileges = minetest.string_to_privs(minetest.setting_get("default_privs")),
}
save_auth_file()
end,
set_password = function(name, password)
assert(type(name) == "string")
assert(type(password) == "string")
if not minetest.auth_table[name] then
minetest.builtin_auth_handler.create_auth(name, password)
else
minetest.log('info', "Built-in authentication handler setting password of player '"..name.."'")
minetest.auth_table[name].password = password
save_auth_file()
end
end,
set_privileges = function(name, privileges)
assert(type(name) == "string")
assert(type(privileges) == "table")
if not minetest.auth_table[name] then
minetest.builtin_auth_handler.create_auth(name, minetest.get_password_hash(name, minetest.setting_get("default_password")))
end
minetest.auth_table[name].privileges = privileges
save_auth_file()
end
}
function minetest.register_authentication_handler(handler)
if minetest.registered_auth_handler then
error("Add-on authentication handler already registered by "..minetest.registered_auth_handler_modname)
end
minetest.registered_auth_handler = handler
minetest.registered_auth_handler_modname = minetest.get_current_modname()
end
function minetest.get_auth_handler()
if minetest.registered_auth_handler then
return minetest.registered_auth_handler
end
return minetest.builtin_auth_handler
end
function minetest.set_player_password(name, password)
minetest.get_auth_handler().set_password(name, password)
end
function minetest.set_player_privs(name, privs)
minetest.get_auth_handler().set_privileges(name, privs)
end
--
-- Set random seed
--

@ -464,6 +464,8 @@ dump2(obj, name="_", dumped={})
^ Return object serialized as a string, handles reference loops
dump(obj, dumped={})
^ Return object serialized as a string
string:split(separator)
string:trim()
minetest namespace reference
-----------------------------
@ -480,6 +482,7 @@ minetest.log(line)
minetest.log(loglevel, line)
^ loglevel one of "error", "action", "info", "verbose"
Registration functions: (Call these only at load time)
minetest.register_entity(name, prototype table)
minetest.register_abm(abm definition)
minetest.register_node(name, node definition)
@ -487,7 +490,6 @@ minetest.register_tool(name, item definition)
minetest.register_craftitem(name, item definition)
minetest.register_alias(name, convert_to)
minetest.register_craft(recipe)
minetest.register_globalstep(func(dtime))
minetest.register_on_placenode(func(pos, newnode, placer))
minetest.register_on_dignode(func(pos, oldnode, digger))
@ -500,24 +502,40 @@ minetest.register_on_respawnplayer(func(ObjectRef))
^ currently called _before_ repositioning of player occurs
minetest.register_on_chat_message(func(name, message))
minetest.register_chatcommand(cmd, chatcommand definition)
minetest.register_privilege(name, description)
minetest.register_authentication_handler(handler)
^ See minetest.builtin_auth_handler in builtin.lua for reference
minetest.add_to_creative_inventory(itemstring)
Setting-related:
minetest.setting_get(name) -> string or nil
minetest.setting_getbool(name) -> boolean value or nil
minetest.add_to_creative_inventory(itemstring)
Authentication:
minetest.get_password_hash(name, raw_password)
minetest.set_player_password(name, password_hash)
minetest.string_to_privs(str) -> {priv1=true,...}
minetest.privs_to_string(privs) -> "priv1,priv2,..."
minetest.set_player_privs(name, {priv1=true,...})
minetest.get_player_privs(name) -> {priv1=true,...}
minetest.check_player_privs(name, {priv1=true,...}) -> bool, missing_privs
Chat:
minetest.chat_send_all(text)
minetest.chat_send_player(name, text)
minetest.get_player_privs(name) -> set of privs
minetest.check_player_privs(name, {priv1=true,...}) -> bool, missing_privs
Inventory:
minetest.get_inventory(location) -> InvRef
^ location = eg. {type="player", name="celeron55"}
{type="node", pos={x=, y=, z=}}
Sounds:
minetest.sound_play(spec, parameters) -> handle
^ spec = SimpleSoundSpec
^ parameters = sound parameter table
minetest.sound_stop(handle)
Timing:
minetest.after(time, func, param)
^ Call function after time seconds
^ param is optional; to pass multiple parameters, pass a table.

@ -173,7 +173,6 @@ set(common_SRCS
mapgen.cpp
content_nodemeta.cpp
content_mapnode.cpp
auth.cpp
collision.cpp
nodemetadata.cpp
serverobject.cpp

@ -1,306 +0,0 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "auth.h"
#include <fstream>
#include <jmutexautolock.h>
//#include "main.h" // for g_settings
#include <sstream>
#include "strfnd.h"
#include "log.h"
std::set<std::string> privsToSet(u64 privs)
{
std::set<std::string> s;
if(privs & PRIV_INTERACT)
s.insert("interact");
if(privs & PRIV_TELEPORT)
s.insert("teleport");
if(privs & PRIV_SETTIME)
s.insert("settime");
if(privs & PRIV_PRIVS)
s.insert("privs");
if(privs & PRIV_SERVER)
s.insert("server");
if(privs & PRIV_SHOUT)
s.insert("shout");
if(privs & PRIV_BAN)
s.insert("ban");
if(privs & PRIV_GIVE)
s.insert("give");
if(privs & PRIV_PASSWORD)
s.insert("password");
return s;
}
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::string privsToString(u64 privs)
{
std::ostringstream os(std::ios_base::binary);
if(privs & PRIV_INTERACT)
os<<"interact,";
if(privs & PRIV_TELEPORT)
os<<"teleport,";
if(privs & PRIV_SETTIME)
os<<"settime,";
if(privs & PRIV_PRIVS)
os<<"privs,";
if(privs & PRIV_SERVER)
os<<"server,";
if(privs & PRIV_SHOUT)
os<<"shout,";
if(privs & PRIV_BAN)
os<<"ban,";
if(privs & PRIV_GIVE)
os<<"give,";
if(privs & PRIV_PASSWORD)
os<<"password,";
if(os.tellp())
{
// Drop the trailing comma. (Why on earth can't
// you truncate a C++ stream anyway???)
std::string tmp = os.str();
return tmp.substr(0, tmp.length() -1);
}
return os.str();
}
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::string str)
{
u64 privs=0;
Strfnd f(str);
while(f.atend() == false)
{
std::string s = trim(f.next(","));
if(s == "build")
privs |= PRIV_INTERACT;
else if(s == "interact")
privs |= PRIV_INTERACT;
else if(s == "teleport")
privs |= PRIV_TELEPORT;
else if(s == "settime")
privs |= PRIV_SETTIME;
else if(s == "privs")
privs |= PRIV_PRIVS;
else if(s == "server")
privs |= PRIV_SERVER;
else if(s == "shout")
privs |= PRIV_SHOUT;
else if(s == "ban")
privs |= PRIV_BAN;
else if(s == "give")
privs |= PRIV_GIVE;
else if(s == "password")
privs |= PRIV_PASSWORD;
else
return PRIV_INVALID;
}
return privs;
}
AuthManager::AuthManager(const std::string &authfilepath):
m_authfilepath(authfilepath),
m_modified(false)
{
m_mutex.Init();
try{
load();
}
catch(SerializationError &e)
{
infostream<<"WARNING: AuthManager: creating "
<<m_authfilepath<<std::endl;
}
}
AuthManager::~AuthManager()
{
save();
}
void AuthManager::load()
{
JMutexAutoLock lock(m_mutex);
infostream<<"AuthManager: loading from "<<m_authfilepath<<std::endl;
std::ifstream is(m_authfilepath.c_str(), std::ios::binary);
if(is.good() == false)
{
infostream<<"AuthManager: failed loading from "<<m_authfilepath<<std::endl;
throw SerializationError("AuthManager::load(): Couldn't open file");
}
for(;;)
{
if(is.eof() || is.good() == false)
break;
// Read a line
std::string line;
std::getline(is, line, '\n');
std::istringstream iss(line);
// Read name
std::string name;
std::getline(iss, name, ':');
// Read password
std::string pwd;
std::getline(iss, pwd, ':');
// Read privileges
std::string stringprivs;
std::getline(iss, stringprivs, ':');
u64 privs = stringToPrivs(stringprivs);
// Store it
AuthData ad;
ad.pwd = pwd;
ad.privs = privs;
m_authdata[name] = ad;
}
m_modified = false;
}
void AuthManager::save()
{
JMutexAutoLock lock(m_mutex);
infostream<<"AuthManager: saving to "<<m_authfilepath<<std::endl;
std::ofstream os(m_authfilepath.c_str(), std::ios::binary);
if(os.good() == false)
{
infostream<<"AuthManager: failed saving to "<<m_authfilepath<<std::endl;
throw SerializationError("AuthManager::save(): Couldn't open file");
}
for(core::map<std::string, AuthData>::Iterator
i = m_authdata.getIterator();
i.atEnd()==false; i++)
{
std::string name = i.getNode()->getKey();
if(name == "")
continue;
AuthData ad = i.getNode()->getValue();
os<<name<<":"<<ad.pwd<<":"<<privsToString(ad.privs)<<"\n";
}
m_modified = false;
}
bool AuthManager::exists(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
return false;
return true;
}
void AuthManager::set(const std::string &username, AuthData ad)
{
JMutexAutoLock lock(m_mutex);
m_authdata[username] = ad;
m_modified = true;
}
void AuthManager::add(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
m_authdata[username] = AuthData();
m_modified = true;
}
std::string AuthManager::getPassword(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
return n->getValue().pwd;
}
void AuthManager::setPassword(const std::string &username,
const std::string &password)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
AuthData ad = n->getValue();
ad.pwd = password;
n->setValue(ad);
m_modified = true;
}
u64 AuthManager::getPrivs(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
return n->getValue().privs;
}
void AuthManager::setPrivs(const std::string &username, u64 privs)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
AuthData ad = n->getValue();
ad.privs = privs;
n->setValue(ad);
m_modified = true;
}
bool AuthManager::isModified()
{
JMutexAutoLock lock(m_mutex);
return m_modified;
}

@ -1,107 +0,0 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef AUTH_HEADER
#define AUTH_HEADER
#include <set>
#include <string>
#include <jthread.h>
#include <jmutex.h>
#include "irrlichttypes.h"
#include "exceptions.h"
// Player privileges. These form a bitmask stored in the privs field
// of the player, and define things they're allowed to do. See also
// the static methods Player::privsToString and stringToPrivs that
// convert these to human-readable form.
const u64 PRIV_INTERACT = 1; // Can interact
const u64 PRIV_TELEPORT = 2; // Can teleport
const u64 PRIV_SETTIME = 4; // Can set the time
const u64 PRIV_PRIVS = 8; // Can grant and revoke privileges
const u64 PRIV_SERVER = 16; // Can manage the server (e.g. shutodwn
// ,settings)
const u64 PRIV_SHOUT = 32; // Can broadcast chat messages to all
// players
const u64 PRIV_BAN = 64; // Can ban players
const u64 PRIV_GIVE = 128; // Can give stuff
const u64 PRIV_PASSWORD = 256; // Can set other players' passwords
// Default privileges - these can be overriden for new players using the
// config option "default_privs" - however, this value still applies for
// players that existed before the privileges system was added.
const u64 PRIV_DEFAULT = PRIV_INTERACT|PRIV_SHOUT;
const u64 PRIV_ALL = 0x7FFFFFFFFFFFFFFFULL;
const u64 PRIV_INVALID = 0x8000000000000000ULL;
std::set<std::string> privsToSet(u64 privs);
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::string privsToString(u64 privs);
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::string str);
struct AuthData
{
std::string pwd;
u64 privs;
AuthData():
privs(PRIV_DEFAULT)
{
}
};
class AuthNotFoundException : public BaseException
{
public:
AuthNotFoundException(const char *s):
BaseException(s)
{}
};
class AuthManager
{
public:
AuthManager(const std::string &authfilepath);
~AuthManager();
void load();
void save();
bool exists(const std::string &username);
void set(const std::string &username, AuthData ad);
void add(const std::string &username);
std::string getPassword(const std::string &username);
void setPassword(const std::string &username,
const std::string &password);
u64 getPrivs(const std::string &username);
void setPrivs(const std::string &username, u64 privs);
bool isModified();
private:
JMutex m_mutex;
std::string m_authfilepath;
core::map<std::string, AuthData> m_authdata;
bool m_modified;
};
#endif

@ -141,14 +141,14 @@ static Server* get_server(lua_State *L)
return server;
}
static ServerEnvironment* get_env(lua_State *L)
/*static ServerEnvironment* get_env(lua_State *L)
{
// Get environment from registry
lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env");
ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1);
lua_pop(L, 1);
return env;
}
}*/
static void objectref_get(lua_State *L, u16 id)
{
@ -647,6 +647,27 @@ static void read_groups(lua_State *L, int index,
}
}
/*
Privileges
*/
static void read_privileges(lua_State *L, int index,
std::set<std::string> &result)
{
result.clear();
lua_pushnil(L);
if(index < 0)
index -= 1;
while(lua_next(L, index) != 0){
// key at index -2 and value at index -1
std::string key = luaL_checkstring(L, -2);
bool value = lua_toboolean(L, -1);
if(value)
result.insert(key);
// removes value, keeps key for next iteration
lua_pop(L, 1);
}
}
/*
ToolCapabilities
*/
@ -3837,8 +3858,7 @@ static int l_get_player_privs(lua_State *L)
// Do it
lua_newtable(L);
int table = lua_gettop(L);
u64 privs_i = server->getPlayerEffectivePrivs(name);
std::set<std::string> privs_s = privsToSet(privs_i);
std::set<std::string> privs_s = server->getPlayerEffectivePrivs(name);
for(std::set<std::string>::const_iterator
i = privs_s.begin(); i != privs_s.end(); i++){
lua_pushboolean(L, true);
@ -3954,6 +3974,17 @@ static int l_is_singleplayer(lua_State *L)
return 1;
}
// get_password_hash(name, raw_password)
static int l_get_password_hash(lua_State *L)
{
std::string name = luaL_checkstring(L, 1);
std::string raw_password = luaL_checkstring(L, 2);
std::string hash = translatePassword(name,
narrow_to_wide(raw_password));
lua_pushstring(L, hash.c_str());
return 1;
}
static const struct luaL_Reg minetest_f [] = {
{"debug", l_debug},
{"log", l_log},
@ -3974,6 +4005,7 @@ static const struct luaL_Reg minetest_f [] = {
{"sound_play", l_sound_play},
{"sound_stop", l_sound_stop},
{"is_singleplayer", l_is_singleplayer},
{"get_password_hash", l_get_password_hash},
{NULL, NULL}
};
@ -4421,6 +4453,10 @@ void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player)
void scriptapi_get_creative_inventory(lua_State *L, ServerActiveObject *player)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
Inventory *inv = player->getInventory();
assert(inv);
@ -4430,6 +4466,91 @@ void scriptapi_get_creative_inventory(lua_State *L, ServerActiveObject *player)
inventory_set_list_from_lua(inv, "main", L, -1, PLAYER_INVENTORY_SIZE);
}
static void get_auth_handler(lua_State *L)
{
lua_getglobal(L, "minetest");
lua_getfield(L, -1, "registered_auth_handler");
if(lua_isnil(L, -1)){
lua_pop(L, 1);
lua_getfield(L, -1, "builtin_auth_handler");
}
if(lua_type(L, -1) != LUA_TTABLE)
throw LuaError(L, "Authentication handler table not valid");
}
bool scriptapi_get_auth(lua_State *L, const std::string &playername,
std::string *dst_password, std::set<std::string> *dst_privs)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
get_auth_handler(L);
lua_getfield(L, -1, "get_auth");
if(lua_type(L, -1) != LUA_TFUNCTION)
throw LuaError(L, "Authentication handler missing get_auth");
lua_pushstring(L, playername.c_str());
if(lua_pcall(L, 1, 1, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
// nil = login not allowed
if(lua_isnil(L, -1))
return false;
luaL_checktype(L, -1, LUA_TTABLE);
std::string password;
bool found = getstringfield(L, -1, "password", password);
if(!found)
throw LuaError(L, "Authentication handler didn't return password");
if(dst_password)
*dst_password = password;
lua_getfield(L, -1, "privileges");
if(!lua_istable(L, -1))
throw LuaError(L,
"Authentication handler didn't return privilege table");
if(dst_privs)
read_privileges(L, -1, *dst_privs);
lua_pop(L, 1);
return true;
}
void scriptapi_create_auth(lua_State *L, const std::string &playername,
const std::string &password)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
get_auth_handler(L);
lua_getfield(L, -1, "create_auth");
if(lua_type(L, -1) != LUA_TFUNCTION)
throw LuaError(L, "Authentication handler missing create_auth");
lua_pushstring(L, playername.c_str());
lua_pushstring(L, password.c_str());
if(lua_pcall(L, 2, 0, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
}
bool scriptapi_set_password(lua_State *L, const std::string &playername,
const std::string &password)
{
realitycheck(L);
assert(lua_checkstack(L, 20));
StackUnroller stack_unroller(L);
get_auth_handler(L);
lua_getfield(L, -1, "set_password");
if(lua_type(L, -1) != LUA_TFUNCTION)
throw LuaError(L, "Authentication handler missing set_password");
lua_pushstring(L, playername.c_str());
lua_pushstring(L, password.c_str());
if(lua_pcall(L, 2, 1, 0))
script_error(L, "error: %s", lua_tostring(L, -1));
return lua_toboolean(L, -1);
}
/*
item callbacks and node callbacks
*/

@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes.h"
#include <string>
#include "mapnode.h"
#include <set>
class Server;
class ServerEnvironment;
@ -60,6 +61,12 @@ bool scriptapi_on_respawnplayer(lua_State *L, ServerActiveObject *player);
void scriptapi_on_joinplayer(lua_State *L, ServerActiveObject *player);
void scriptapi_on_leaveplayer(lua_State *L, ServerActiveObject *player);
void scriptapi_get_creative_inventory(lua_State *L, ServerActiveObject *player);
bool scriptapi_get_auth(lua_State *L, const std::string &playername,
std::string *dst_password, std::set<std::string> *dst_privs);
void scriptapi_create_auth(lua_State *L, const std::string &playername,
const std::string &password);
bool scriptapi_set_password(lua_State *L, const std::string &playername,
const std::string &password);
/* item callbacks */
bool scriptapi_item_on_drop(lua_State *L, ItemStack &item,

@ -142,6 +142,10 @@ void * ServerThread::Thread()
{
m_server->setAsyncFatalError(e.what());
}
catch(LuaError &e)
{
m_server->setAsyncFatalError(e.what());
}
}
END_DEBUG_EXCEPTION_HANDLER(errorstream)
@ -905,7 +909,6 @@ Server::Server(
m_async_fatal_error(""),
m_env(NULL),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_authmanager(path_world+DIR_DELIM+"auth.txt"),
m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
m_lua(NULL),
m_itemdef(createItemDefManager()),
@ -1844,10 +1847,6 @@ void Server::AsyncRunStep()
ScopeProfiler sp(g_profiler, "Server: saving stuff");
// Auth stuff
if(m_authmanager.isModified())
m_authmanager.save();
//Ban stuff
if(m_banmanager.isModified())
m_banmanager.save();
@ -2083,34 +2082,30 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
password[PASSWORD_SIZE-1] = 0;
}
// Add player to auth manager
if(m_authmanager.exists(playername) == false)
{
std::wstring default_password =
std::string checkpwd;
bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
if(!has_auth){
std::wstring raw_default_password =
narrow_to_wide(g_settings->get("default_password"));
std::string translated_default_password =
translatePassword(playername, default_password);
std::string use_password =
translatePassword(playername, raw_default_password);
// If default_password is empty, allow any initial password
if (default_password.length() == 0)
translated_default_password = password;
if (raw_default_password.length() == 0)
use_password = password;
infostream<<"Server: adding player "<<playername
<<" to auth manager"<<std::endl;
m_authmanager.add(playername);
m_authmanager.setPassword(playername, translated_default_password);
m_authmanager.setPrivs(playername,
stringToPrivs(g_settings->get("default_privs")));
m_authmanager.save();
scriptapi_create_auth(m_lua, playername, use_password);
}
std::string checkpwd = m_authmanager.getPassword(playername);
has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
/*infostream<<"Server: Client gave password '"<<password
<<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
if(!has_auth){
SendAccessDenied(m_con, peer_id, L"Not allowed to login");
return;
}
if(password != checkpwd)
{
if(password != checkpwd){
infostream<<"Server: peer_id="<<peer_id
<<": supplied invalid password for "
<<playername<<std::endl;
@ -2131,8 +2126,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Enforce user limit.
// Don't enforce for users that have some admin right
if(m_clients.size() >= g_settings->getU16("max_users") &&
(m_authmanager.getPrivs(playername)
& (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
!checkPriv(playername, "server") &&
!checkPriv(playername, "ban") &&
!checkPriv(playername, "privs") &&
!checkPriv(playername, "password") &&
playername != g_settings->get("name"))
{
actionstream<<"Server: "<<playername<<" tried to join, but there"
@ -2407,7 +2404,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
else if(command == TOSERVER_SIGNNODETEXT)
{
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
if(!checkPriv(player->getName(), "interact"))
return;
/*
u16 command
@ -2519,9 +2516,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Disallow moving items in elsewhere than player's inventory
// if not allowed to interact
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0
&& (!from_inv_is_current_player
|| !to_inv_is_current_player))
if(!checkPriv(player->getName(), "interact") &&
(!from_inv_is_current_player ||
!to_inv_is_current_player))
{
infostream<<"Cannot move outside of player's inventory: "
<<"No interact privilege"<<std::endl;
@ -2530,7 +2527,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
// If player is not an admin, check for ownership of src and dst
if((getPlayerPrivs(player) & PRIV_SERVER) == 0)
if(!checkPriv(player->getName(), "server"))
{
std::string owner_from = getInventoryOwner(ma->from_inv);
if(owner_from != "" && owner_from != player->getName())
@ -2565,13 +2562,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
setInventoryModified(da->from_inv);
// Disallow dropping items if not allowed to interact
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
if(!checkPriv(player->getName(), "interact"))
{
delete a;
return;
}
// If player is not an admin, check for ownership
else if((getPlayerPrivs(player) & PRIV_SERVER) == 0)
else if(!checkPriv(player->getName(), "server"))
{
std::string owner_from = getInventoryOwner(da->from_inv);
if(owner_from != "" && owner_from != player->getName())
@ -2600,7 +2597,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// (ca->craft_inv.name == player->getName());
// Disallow crafting if not allowed to interact
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
if(!checkPriv(player->getName(), "interact"))
{
infostream<<"Cannot craft: "
<<"No interact privilege"<<std::endl;
@ -2609,7 +2606,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
// If player is not an admin, check for ownership of inventory
if((getPlayerPrivs(player) & PRIV_SERVER) == 0)
if(!checkPriv(player->getName(), "server"))
{
std::string owner_craft = getInventoryOwner(ca->craft_inv);
if(owner_craft != "" && owner_craft != player->getName())
@ -2667,10 +2664,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Whether to send to other players
bool send_to_others = false;
// Local player gets all privileges regardless of
// what's set on their account.
u64 privs = getPlayerPrivs(player);
// Parse commands
if(message[0] == L'/')
{
@ -2688,8 +2681,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
paramstring,
this,
m_env,
player,
privs);
player);
std::wstring reply(processServerCommand(ctx));
send_to_sender = ctx->flags & SEND_TO_SENDER;
@ -2705,16 +2697,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
}
else
{
if(privs & PRIV_SHOUT)
{
if(checkPriv(player->getName(), "shout")){
line += L"<";
line += name;
line += L"> ";
line += message;
send_to_others = true;
}
else
{
} else {
line += L"Server: You are not allowed to shout";
send_to_sender = true;
}
@ -2803,15 +2792,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
std::string playername = player->getName();
if(m_authmanager.exists(playername) == false)
{
infostream<<"Server: playername not found in authmanager"<<std::endl;
// Wrong old password supplied!!
SendChatMessage(peer_id, L"playername not found in authmanager");
return;
}
std::string checkpwd = m_authmanager.getPassword(playername);
std::string checkpwd;
scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
if(oldpwd != checkpwd)
{
@ -2821,13 +2803,15 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
return;
}
bool success = scriptapi_set_password(m_lua, playername, newpwd);
if(success){
actionstream<<player->getName()<<" changes password"<<std::endl;
m_authmanager.setPassword(playername, newpwd);
infostream<<"Server: password change successful for "<<playername
<<std::endl;
SendChatMessage(peer_id, L"Password change successful");
} else {
actionstream<<player->getName()<<" tries to change password but "
<<"it fails"<<std::endl;
SendChatMessage(peer_id, L"Password change failed or inavailable");
}
}
else if(command == TOSERVER_PLAYERITEM)
{
@ -2970,11 +2954,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
/*
Make sure the player is allowed to do it
*/
if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
if(!checkPriv(player->getName(), "interact"))
{
infostream<<"Ignoring interaction from player "<<player->getName()
<<" because privileges are "<<getPlayerPrivs(player)
<<std::endl;
<<" (no interact privilege)"<<std::endl;
return;
}
@ -4277,54 +4260,17 @@ std::wstring Server::getStatusString()
return os.str();
}
u64 Server::getPlayerAuthPrivs(const std::string &name)
std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
{
try{
return m_authmanager.getPrivs(name);
}
catch(AuthNotFoundException &e)
{
dstream<<"WARNING: Auth not found for "<<name<<std::endl;
return 0;
}
std::set<std::string> privs;
scriptapi_get_auth(m_lua, name, NULL, &privs);
return privs;
}
void Server::setPlayerAuthPrivs(const std::string &name, u64 privs)
bool Server::checkPriv(const std::string &name, const std::string &priv)
{
try{
return m_authmanager.setPrivs(name, privs);
}
catch(AuthNotFoundException &e)
{
dstream<<"WARNING: Auth not found for "<<name<<std::endl;
}
}
u64 Server::getPlayerEffectivePrivs(const std::string &name)
{
// Local player gets all privileges regardless of
// what's set on their account.
if(m_simple_singleplayer_mode)
return PRIV_ALL;
if(name == g_settings->get("name"))
return PRIV_ALL;
return getPlayerAuthPrivs(name);
}
void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
{
// Add player to auth manager
if(m_authmanager.exists(name) == false)
{
infostream<<"Server: adding player "<<name
<<" to auth manager"<<std::endl;
m_authmanager.add(name);
m_authmanager.setPrivs(name,
stringToPrivs(g_settings->get("default_privs")));
}
// Change password and save
m_authmanager.setPassword(name, translatePassword(name, password));
m_authmanager.save();
std::set<std::string> privs = getPlayerEffectivePrivs(name);
return (privs.count(priv) != 0);
}
// Saves g_settings to configpath given at initialization
@ -4698,14 +4644,6 @@ void Server::handlePeerChanges()
}
}
u64 Server::getPlayerPrivs(Player *player)
{
if(player==NULL)
return 0;
std::string playername = player->getName();
return getPlayerEffectivePrivs(playername);
}
void dedicated_server_loop(Server &server, bool &kill)
{
DSTACK(__FUNCTION_NAME);

@ -27,7 +27,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h"
#include "map.h"
#include "inventory.h"
#include "auth.h"
#include "ban.h"
#include "gamedef.h"
#include "serialization.h" // For SER_FMT_VER_INVALID
@ -500,14 +499,9 @@ public:
s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params);
void stopSound(s32 handle);
// Thread-safe
u64 getPlayerAuthPrivs(const std::string &name);
void setPlayerAuthPrivs(const std::string &name, u64 privs);
u64 getPlayerEffectivePrivs(const std::string &name);
// Changes a player's password, password must be given as plaintext
// If the player doesn't exist, a new entry is added to the auth manager
void setPlayerPassword(const std::string &name, const std::wstring &password);
// Envlock + conlock
std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
bool checkPriv(const std::string &name, const std::string &priv);
// Saves g_settings to configpath given at initialization
void saveConfig();
@ -670,8 +664,6 @@ private:
void handlePeerChange(PeerChange &c);
void handlePeerChanges();
u64 getPlayerPrivs(Player *player);
/*
Variables
*/
@ -710,9 +702,6 @@ private:
// Connected clients (behind the con mutex)
core::map<u16, RemoteClient*> m_clients;
// User authentication
AuthManager m_authmanager;
// Bann checking
BanManager m_banmanager;

@ -38,98 +38,6 @@ void cmd_me(std::wostringstream &os,
ctx->flags |= SEND_TO_OTHERS | SEND_NO_PREFIX;
}
void cmd_privs(std::wostringstream &os,
ServerCommandContext *ctx)
{
if(ctx->parms.size() == 1)
{
// Show our own real privs, without any adjustments
// made for admin status
os<<L"-!- " + narrow_to_wide(privsToString(
ctx->server->getPlayerAuthPrivs(ctx->player->getName())));
return;
}
if((ctx->privs & PRIV_PRIVS) == 0)
{
os<<L"-!- You don't have permission to do that";
return;
}
Player *tp = ctx->env->getPlayer(wide_to_narrow(ctx->parms[1]).c_str());
if(tp == NULL)
{
os<<L"-!- No such player";
return;
}
os<<L"-!- " + narrow_to_wide(privsToString(ctx->server->getPlayerAuthPrivs(tp->getName())));
}
void cmd_grantrevoke(std::wostringstream &os,
ServerCommandContext *ctx)
{
if(ctx->parms.size() != 3)
{
os<<L"-!- Missing parameter";
return;
}
if((ctx->privs & PRIV_PRIVS) == 0)
{
os<<L"-!- You don't have permission to do that";
return;
}
u64 newprivs = stringToPrivs(wide_to_narrow(ctx->parms[2]));
if(newprivs == PRIV_INVALID)
{
os<<L"-!- Invalid privileges specified";
return;
}
Player *tp = ctx->env->getPlayer(wide_to_narrow(ctx->parms[1]).c_str());
if(tp == NULL)
{
os<<L"-!- No such player";
return;
}
std::string playername = wide_to_narrow(ctx->parms[1]);
u64 privs = ctx->server->getPlayerAuthPrivs(playername);
if(ctx->parms[0] == L"grant"){
privs |= newprivs;
actionstream<<ctx->player->getName()<<" grants "
<<wide_to_narrow(ctx->parms[2])<<" to "
<<playername<<std::endl;
std::wstring msg;
msg += narrow_to_wide(ctx->player->getName());
msg += L" granted you the privilege \"";
msg += ctx->parms[2];
msg += L"\"";
ctx->server->notifyPlayer(playername.c_str(), msg);
} else {
privs &= ~newprivs;
actionstream<<ctx->player->getName()<<" revokes "
<<wide_to_narrow(ctx->parms[2])<<" from "
<<playername<<std::endl;
std::wstring msg;
msg += narrow_to_wide(ctx->player->getName());
msg += L" revoked from you the privilege \"";
msg += ctx->parms[2];
msg += L"\"";
ctx->server->notifyPlayer(playername.c_str(), msg);
}
ctx->server->setPlayerAuthPrivs(playername, privs);
os<<L"-!- Privileges change to ";
os<<narrow_to_wide(privsToString(privs));
}
void cmd_time(std::wostringstream &os,
ServerCommandContext *ctx)
{
@ -139,7 +47,7 @@ void cmd_time(std::wostringstream &os,
return;
}
if((ctx->privs & PRIV_SETTIME) ==0)
if(!ctx->server->checkPriv(ctx->player->getName(), "settime"))
{
os<<L"-!- You don't have permission to do that";
return;
@ -156,7 +64,7 @@ void cmd_time(std::wostringstream &os,
void cmd_shutdown(std::wostringstream &os,
ServerCommandContext *ctx)
{
if((ctx->privs & PRIV_SERVER) ==0)
if(!ctx->server->checkPriv(ctx->player->getName(), "server"))
{
os<<L"-!- You don't have permission to do that";
return;
@ -174,7 +82,7 @@ void cmd_shutdown(std::wostringstream &os,
void cmd_setting(std::wostringstream &os,
ServerCommandContext *ctx)
{
if((ctx->privs & PRIV_SERVER) ==0)
if(!ctx->server->checkPriv(ctx->player->getName(), "server"))
{
os<<L"-!- You don't have permission to do that";
return;
@ -198,7 +106,7 @@ void cmd_setting(std::wostringstream &os,
void cmd_teleport(std::wostringstream &os,
ServerCommandContext *ctx)
{
if((ctx->privs & PRIV_TELEPORT) ==0)
if(!ctx->server->checkPriv(ctx->player->getName(), "teleport"))
{
os<<L"-!- You don't have permission to do that";
return;
@ -241,7 +149,7 @@ void cmd_teleport(std::wostringstream &os,
void cmd_banunban(std::wostringstream &os, ServerCommandContext *ctx)
{
if((ctx->privs & PRIV_BAN) == 0)
if(!ctx->server->checkPriv(ctx->player->getName(), "ban"))
{
os<<L"-!- You don't have permission to do that";
return;
@ -288,62 +196,10 @@ void cmd_banunban(std::wostringstream &os, ServerCommandContext *ctx)
}
}
void cmd_setclearpassword(std::wostringstream &os,
ServerCommandContext *ctx)
{
if((ctx->privs & PRIV_PASSWORD) == 0)
{
os<<L"-!- You don't have permission to do that";
return;
}
std::string playername;
std::wstring password;
if(ctx->parms[0] == L"setpassword")
{
if(ctx->parms.size() != 3)
{
os<<L"-!- Missing parameter";
return;
}
playername = wide_to_narrow(ctx->parms[1]);
password = ctx->parms[2];
actionstream<<ctx->player->getName()<<" sets password of "
<<playername<<std::endl;
}
else
{
// clearpassword
if(ctx->parms.size() != 2)
{
os<<L"-!- Missing parameter";
return;
}
playername = wide_to_narrow(ctx->parms[1]);
password = L"";
actionstream<<ctx->player->getName()<<" clears password of"
<<playername<<std::endl;
}
ctx->server->setPlayerPassword(playername, password);
std::wostringstream msg;
msg<<ctx->player->getName()<<L" changed your password";
ctx->server->notifyPlayer(playername.c_str(), msg.str());
os<<L"-!- Password change for "<<narrow_to_wide(playername)<<" successful";
}
void cmd_clearobjects(std::wostringstream &os,
ServerCommandContext *ctx)
{
if((ctx->privs & PRIV_SERVER) ==0)
if(!ctx->server->checkPriv(ctx->player->getName(), "server"))
{
os<<L"-!- You don't have permission to do that";
return;
@ -378,10 +234,6 @@ std::wstring processServerCommand(ServerCommandContext *ctx)
if(ctx->parms[0] == L"status")
cmd_status(os, ctx);
else if(ctx->parms[0] == L"privs")
cmd_privs(os, ctx);
else if(ctx->parms[0] == L"grant" || ctx->parms[0] == L"revoke")
cmd_grantrevoke(os, ctx);
else if(ctx->parms[0] == L"time")
cmd_time(os, ctx);
else if(ctx->parms[0] == L"shutdown")
@ -392,8 +244,6 @@ std::wstring processServerCommand(ServerCommandContext *ctx)
cmd_teleport(os, ctx);
else if(ctx->parms[0] == L"ban" || ctx->parms[0] == L"unban")
cmd_banunban(os, ctx);
else if(ctx->parms[0] == L"setpassword" || ctx->parms[0] == L"clearpassword")
cmd_setclearpassword(os, ctx);
else if(ctx->parms[0] == L"me")
cmd_me(os, ctx);
else if(ctx->parms[0] == L"clearobjects")

@ -36,9 +36,6 @@ struct ServerCommandContext
Server* server;
ServerEnvironment *env;
Player* player;
// Effective privs for the player, which may be different to their
// stored ones - e.g. if they are named in the config as an admin.
u64 privs;
u32 flags;
ServerCommandContext(
@ -46,10 +43,9 @@ struct ServerCommandContext
std::wstring paramstring,
Server* server,
ServerEnvironment *env,
Player* player,
u64 privs)
Player* player)
: parms(parms), paramstring(paramstring),
server(server), env(env), player(player), privs(privs)
server(server), env(env), player(player)
{
}