diff options
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/player.cpp | 67 | ||||
-rw-r--r-- | src/player.h | 33 | ||||
-rw-r--r-- | src/server.cpp | 96 | ||||
-rw-r--r-- | src/server.h | 23 | ||||
-rw-r--r-- | src/servercommand.cpp | 231 | ||||
-rw-r--r-- | src/servercommand.h | 58 | ||||
-rw-r--r-- | src/utility.h | 19 |
8 files changed, 457 insertions, 71 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8e6f54303..42260b3ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -69,6 +69,7 @@ set(common_SRCS connection.cpp environment.cpp server.cpp + servercommand.cpp socket.cpp mapblock.cpp mapsector.cpp diff --git a/src/player.cpp b/src/player.cpp index 64780de75..e568d7dee 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -23,6 +23,55 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "constants.h" #include "utility.h" +// Convert a privileges value into a human-readable string, +// with each component separated by a comma. +std::wstring privsToString(u64 privs) +{ + std::wostringstream os(std::ios_base::binary); + if(privs & PRIV_BUILD) + os<<L"build,"; + if(privs & PRIV_TELEPORT) + os<<L"teleport,"; + if(privs & PRIV_SETTIME) + os<<L"settime,"; + if(privs & PRIV_PRIVS) + os<<L"privs,"; + if(os.tellp()) + { + // Drop the trailing comma. (Why on earth can't + // you truncate a C++ stream anyway???) + std::wstring 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::wstring str) +{ + u64 privs=0; + std::vector<std::wstring> pr; + pr=str_split(str, ','); + for(std::vector<std::wstring>::iterator i = pr.begin(); + i != pr.end(); ++i) + { + if(*i == L"build") + privs |= PRIV_BUILD; + else if(*i == L"teleport") + privs |= PRIV_TELEPORT; + else if(*i == L"settime") + privs |= PRIV_SETTIME; + else if(*i == L"privs") + privs |= PRIV_PRIVS; + else + return PRIV_INVALID; + } + return privs; +} + + Player::Player(): touching_ground(false), in_water(false), @@ -34,7 +83,8 @@ Player::Player(): m_pitch(0), m_yaw(0), m_speed(0,0,0), - m_position(0,0,0) + m_position(0,0,0), + privs(PRIV_DEFAULT) { updateName("<not set>"); resetInventory(); @@ -100,6 +150,7 @@ void Player::serialize(std::ostream &os) args.setV3F("position", m_position); args.setBool("craftresult_is_preview", craftresult_is_preview); args.setS32("hp", hp); + args.setU64("privs", privs); args.writeLines(os); @@ -141,6 +192,20 @@ void Player::deSerialize(std::istream &is) }catch(SettingNotFoundException &e){ hp = 20; } + try{ + std::string sprivs = args.get("privs"); + if(sprivs == "all") + { + privs = PRIV_ALL; + } + else + { + std::istringstream ss(sprivs); + ss>>privs; + } + }catch(SettingNotFoundException &e){ + privs = PRIV_DEFAULT; + } inventory.deSerialize(is); } diff --git a/src/player.h b/src/player.h index f70b52fe7..be93766fd 100644 --- a/src/player.h +++ b/src/player.h @@ -28,11 +28,38 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.," +// 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_BUILD = 1; // Can build - i.e. modify the world + // (not enforced yet) +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_DEFAULT = PRIV_BUILD; +const u64 PRIV_ALL = 0x7FFFFFFFFFFFFFFFULL; +const u64 PRIV_INVALID = 0x8000000000000000ULL; + +// Convert a privileges value into a human-readable string, +// with each component separated by a comma. +std::wstring 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::wstring str); + + class Map; class Player { public: + + Player(); virtual ~Player(); @@ -123,6 +150,9 @@ public: u16 hp; + // Player's privileges - a bitmaps of PRIV_xxxx. + u64 privs; + u16 peer_id; protected: @@ -131,6 +161,9 @@ protected: f32 m_yaw; v3f m_speed; v3f m_position; + +public: + }; /* diff --git a/src/server.cpp b/src/server.cpp index b5a38aa06..d3ca32ac7 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "materials.h" #include "mineral.h" #include "config.h" +#include "servercommand.h" #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0) @@ -1994,6 +1995,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(datasize < 13) return; + if((player->privs & PRIV_BUILD) == 0) + return; + /* [0] u16 command [2] u8 button (0=left, 1=right) @@ -2075,6 +2079,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(datasize < 7) return; + if((player->privs & PRIV_BUILD) == 0) + return; + /* length: 7 [0] u16 command @@ -2272,6 +2279,10 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) cannot_remove_node = true; } + // Make sure the player is allowed to do it + if((player->privs & PRIV_BUILD) == 0) + cannot_remove_node = true; + /* If node can't be removed, set block to be re-sent to client and quit. @@ -2418,7 +2429,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) try{ // Don't add a node if this is not a free space MapNode n2 = m_env.getMap().getNode(p_over); - if(content_buildable_to(n2.d) == false) + if(content_buildable_to(n2.d) == false + || (player->privs & PRIV_BUILD) ==0) { // Client probably has wrong data. // Set block not sent, so that client will get @@ -2615,6 +2627,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) #endif else if(command == TOSERVER_SIGNTEXT) { + if((player->privs & PRIV_BUILD) == 0) + return; /* u16 command v3s16 blockpos @@ -2672,6 +2686,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } else if(command == TOSERVER_SIGNNODETEXT) { + if((player->privs & PRIV_BUILD) == 0) + return; /* u16 command v3s16 p @@ -2853,71 +2869,19 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) line += L"Server: "; message = message.substr(commandprefix.size()); - // Get player name as narrow string - std::string name_s = player->getName(); - // Convert message to narrow string - std::string message_s = wide_to_narrow(message); - // Operator is the single name defined in config. - std::string operator_name = g_settings.get("name"); - bool is_operator = (operator_name != "" && - wide_to_narrow(name) == operator_name); - bool valid_command = false; - if(message_s == "help") - { - line += L"-!- Available commands: "; - line += L"status "; - if(is_operator) - { - line += L"shutdown setting time "; - } - else - { - } - send_to_sender = true; - valid_command = true; - } - else if(message_s == "status") - { - line = getStatusString(); - send_to_sender = true; - valid_command = true; - } - else if(is_operator) - { - if(message_s == "shutdown") - { - dstream<<DTIME<<" Server: Operator requested shutdown." - <<std::endl; - m_shutdown_requested.set(true); - - line += L"*** Server shutting down (operator request)"; - send_to_sender = true; - valid_command = true; - } - else if(message_s.substr(0,8) == "setting ") - { - std::string confline = message_s.substr(8); - g_settings.parseConfigLine(confline); - line += L"-!- Setting changed."; - send_to_sender = true; - valid_command = true; - } - else if(message_s.substr(0,5) == "time ") - { - u32 time = stoi(message_s.substr(5)); - m_time_of_day.set(time); - m_time_of_day_send_timer = 0; - line += L"-!- time_of_day changed."; - send_to_sender = true; - valid_command = true; - } - } - - if(valid_command == false) - { - line += L"-!- Invalid command: " + message; - send_to_sender = true; - } + + ServerCommandContext *ctx = new ServerCommandContext( + str_split(message, L' '), + this, + &m_env, + player + ); + + line += processServerCommand(ctx); + send_to_sender = ctx->flags & 1; + send_to_others = ctx->flags & 2; + delete ctx; + } else { diff --git a/src/server.h b/src/server.h index 4603f98ed..d8b47aef9 100644 --- a/src/server.h +++ b/src/server.h @@ -387,6 +387,12 @@ public: return time_to_daynight_ratio(m_time_of_day.get()); } + void setTimeOfDay(u32 time) + { + m_time_of_day.set(time); + m_time_of_day_send_timer = 0; + } + bool getShutdownRequested() { return m_shutdown_requested.get(); @@ -405,6 +411,19 @@ public: Inventory* getInventory(InventoryContext *c, std::string id); void inventoryModified(InventoryContext *c, std::string id); + // Connection must be locked when called + std::wstring getStatusString(); + + void requestShutdown(void) + { + m_shutdown_requested.set(true); + } + + + // Envlock and conlock should be locked when calling this + void SendMovePlayer(Player *player); + + private: // Virtual methods from con::PeerHandler. @@ -429,7 +448,6 @@ private: void SendChatMessage(u16 peer_id, const std::wstring &message); void BroadcastChatMessage(const std::wstring &message); void SendPlayerHP(Player *player); - void SendMovePlayer(Player *player); /* Send a node removal/addition event to all clients except ignore_id. Additionally, if far_players!=NULL, players further away than @@ -455,9 +473,6 @@ private: // When called, connection mutex should be locked RemoteClient* getClient(u16 peer_id); - // Connection must be locked when called - std::wstring getStatusString(); - /* Get a player from memory or creates one. If player is already connected, return NULL diff --git a/src/servercommand.cpp b/src/servercommand.cpp new file mode 100644 index 000000000..215dc0d27 --- /dev/null +++ b/src/servercommand.cpp @@ -0,0 +1,231 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> +Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.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 "servercommand.h" +#include "utility.h" + +void cmd_status(std::wostringstream &os, + ServerCommandContext *ctx) +{ + os<<ctx->server->getStatusString(); +} + +void cmd_privs(std::wostringstream &os, + ServerCommandContext *ctx) +{ + if(ctx->parms.size() == 1) + { + os<<L"-!- " + privsToString(ctx->player->privs); + return; + } + + if((ctx->player->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"-!- " + privsToString(tp->privs); +} + +void cmd_grantrevoke(std::wostringstream &os, + ServerCommandContext *ctx) +{ + if(ctx->parms.size() != 3) + { + os<<L"-!- Missing parameter"; + return; + } + + if((ctx->player->privs & PRIV_PRIVS) == 0) + { + os<<L"-!- You don't have permission to do that"; + return; + } + + u64 newprivs = stringToPrivs(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; + } + + if(ctx->parms[0] == L"grant") + tp->privs |= newprivs; + else + tp->privs &= ~newprivs; + + os<<L"-!- Privileges change to "; + os<<privsToString(tp->privs); +} + +void cmd_time(std::wostringstream &os, + ServerCommandContext *ctx) +{ + if(ctx->parms.size() != 2) + { + os<<L"-!- Missing parameter"; + return; + } + + if((ctx->player->privs & PRIV_SETTIME) ==0) + { + os<<L"-!- You don't have permission to do that"; + return; + } + + u32 time = stoi(wide_to_narrow(ctx->parms[1])); + ctx->server->setTimeOfDay(time); + os<<L"-!- time_of_day changed."; +} + +void cmd_shutdown(std::wostringstream &os, + ServerCommandContext *ctx) +{ + if((ctx->player->privs & PRIV_SERVER) ==0) + { + os<<L"-!- You don't have permission to do that"; + return; + } + + dstream<<DTIME<<" Server: Operator requested shutdown." + <<std::endl; + ctx->server->requestShutdown(); + + os<<L"*** Server shutting down (operator request)"; + ctx->flags |= 2; +} + +void cmd_setting(std::wostringstream &os, + ServerCommandContext *ctx) +{ + if((ctx->player->privs & PRIV_SERVER) ==0) + { + os<<L"-!- You don't have permission to do that"; + return; + } + + std::string confline = wide_to_narrow(ctx->parms[1] + L" = " + ctx->parms[2]); + g_settings.parseConfigLine(confline); + os<< L"-!- Setting changed."; +} + +void cmd_teleport(std::wostringstream &os, + ServerCommandContext *ctx) +{ + if((ctx->player->privs & PRIV_TELEPORT) ==0) + { + os<<L"-!- You don't have permission to do that"; + return; + } + + if(ctx->parms.size() != 2) + { + os<<L"-!- Missing parameter"; + return; + } + + std::vector<std::wstring> coords = str_split(ctx->parms[1], L','); + if(coords.size() != 3) + { + os<<L"-!- You can only specify coordinates currently"; + return; + } + + v3f dest(stoi(coords[0])*10, stoi(coords[1])*10, stoi(coords[2])*10); + ctx->player->setPosition(dest); + ctx->server->SendMovePlayer(ctx->player); + + os<< L"-!- Teleported."; +} + + +std::wstring processServerCommand(ServerCommandContext *ctx) +{ + + std::wostringstream os(std::ios_base::binary); + ctx->flags = 1; // Default, unless we change it. + + u64 privs = ctx->player->privs; + + if(ctx->parms.size() == 0 || ctx->parms[0] == L"help") + { + os<<L"-!- Available commands: "; + os<<L"status privs "; + if(privs & PRIV_SERVER) + os<<L"shutdown setting "; + if(privs & PRIV_SETTIME) + os<<L" time"; + if(privs & PRIV_TELEPORT) + os<<L" teleport"; + if(privs & PRIV_PRIVS) + os<<L" grant revoke"; + } + else 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") + { + cmd_shutdown(os, ctx); + } + else if(ctx->parms[0] == L"setting") + { + cmd_setting(os, ctx); + } + else if(ctx->parms[0] == L"teleport") + { + cmd_teleport(os, ctx); + } + else + { + os<<L"-!- Invalid command: " + ctx->parms[0]; + } + return os.str(); +} + + diff --git a/src/servercommand.h b/src/servercommand.h new file mode 100644 index 000000000..bc7823c66 --- /dev/null +++ b/src/servercommand.h @@ -0,0 +1,58 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> +Copyright (C) 2011 Ciaran Gultnieks <ciaran@ciarang.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 SERVERCOMMAND_HEADER +#define SERVERCOMMAND_HEADER + +#include <vector> +#include <sstream> +#include "common_irrlicht.h" +#include "player.h" +#include "server.h" + +struct ServerCommandContext +{ + + std::vector<std::wstring> parms; + Server* server; + ServerEnvironment *env; + Player* player; + u32 flags; + + ServerCommandContext( + std::vector<std::wstring> parms, + Server* server, + ServerEnvironment *env, + Player* player) + : parms(parms), server(server), env(env), player(player) + { + } + +}; + +// Process a command sent from a client. The environment and connection +// should be locked when this is called. +// Returns a response message, to be dealt with according to the flags set +// in the context. +std::wstring processServerCommand(ServerCommandContext *ctx); + +#endif + + diff --git a/src/utility.h b/src/utility.h index 12d732bea..326ebf161 100644 --- a/src/utility.h +++ b/src/utility.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <fstream> #include <string> #include <sstream> +#include <vector> #include <jthread.h> #include <jmutex.h> #include <jmutexautolock.h> @@ -731,6 +732,19 @@ inline std::string wide_to_narrow(const std::wstring& wcs) return *mbs; } +// Split a string using the given delimiter. Returns a vector containing +// the component parts. +inline std::vector<std::wstring> str_split(const std::wstring &str, wchar_t delimiter) +{ + std::vector<std::wstring> parts; + std::wstringstream sstr(str); + std::wstring part; + while(std::getline(sstr, part, delimiter)) + parts.push_back(part); + return parts; +} + + /* See test.cpp for example cases. wraps degrees to the range of -360...360 @@ -791,6 +805,11 @@ inline s32 stoi(std::string s) return atoi(s.c_str()); } +inline s32 stoi(std::wstring s) +{ + return atoi(wide_to_narrow(s).c_str()); +} + inline float stof(std::string s) { float f; |