player passwords and privileges in world/auth.txt
authorPerttu Ahola <celeron55@gmail.com>
Sun, 29 May 2011 18:11:16 +0000 (21:11 +0300)
committerPerttu Ahola <celeron55@gmail.com>
Sun, 29 May 2011 18:11:16 +0000 (21:11 +0300)
--HG--
extra : rebase_source : 7260636295d9068fbeeddf4143c89f2b8a91446c

22 files changed:
.hgignore
doc/changelog.txt
minetest.conf.example
src/CMakeLists.txt
src/auth.cpp [new file with mode: 0644]
src/auth.h [new file with mode: 0644]
src/client.cpp
src/client.h
src/clientserver.h
src/defaultsettings.cpp
src/environment.cpp
src/environment.h
src/game.cpp
src/guiMainMenu.cpp
src/main.cpp
src/player.cpp
src/player.h
src/server.cpp
src/server.h
src/servercommand.cpp
src/servermain.cpp
src/utility.h

index 58a32bafba88d3eeb1bceb2f544f10d8fdc292c5..0870e3c674d7e96310acae24938613b764ff7a65 100644 (file)
--- a/.hgignore
+++ b/.hgignore
@@ -1,4 +1,5 @@
 map/*
+world/*
 CMakeFiles/*
 src/CMakeFiles/*
 src/Makefile
index f43a68f123e5c035705ee8d6608643ab45355041..cb3594a984a746b9d1f950f7045ee31ee2bf09dc 100644 (file)
@@ -13,6 +13,7 @@ X:
 - Slightly updated map format
 - Player passwords
 - All textures first searched from texture_path
+- Map directory ("map") has been renamed to "world" (just rename it to load an old world)
 
 2011-04-24:
 - Smooth lighting with simple ambient occlusion
index acdc19b0ad92d677dd0d66c5c1a50df12fc9c120..5e6393228bd58ee82e66437598ac454af44c8d3e 100644 (file)
 
 #enable_damage = false
 
+#default_password = 
+
+# Available privileges: build, teleport, settime, privs, shout
+#default_privs = build, shout
+
 # Gives some stuff to players at the beginning
 #give_initial_stuff = false
 
index 49982d310e62565989a56b628e022c0ed849a013..f912f68cd2862c0569ab927bade352a36f14ead3 100644 (file)
@@ -50,6 +50,7 @@ configure_file(
 )
 
 set(common_SRCS
+       auth.cpp
        collision.cpp
        nodemetadata.cpp
        serverobject.cpp
diff --git a/src/auth.cpp b/src/auth.cpp
new file mode 100644 (file)
index 0000000..49985e6
--- /dev/null
@@ -0,0 +1,244 @@
+/*
+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 "debug.h"
+
+// 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_BUILD)
+               os<<"build,";
+       if(privs & PRIV_TELEPORT)
+               os<<"teleport,";
+       if(privs & PRIV_SETTIME)
+               os<<"settime,";
+       if(privs & PRIV_PRIVS)
+               os<<"privs,";
+       if(privs & PRIV_SHOUT)
+               os<<"shout,";
+       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_BUILD;
+               else if(s == "teleport")
+                       privs |= PRIV_TELEPORT;
+               else if(s == "settime")
+                       privs |= PRIV_SETTIME;
+               else if(s == "privs")
+                       privs |= PRIV_PRIVS;
+               else if(s == "shout")
+                       privs |= PRIV_SHOUT;
+               else
+                       return PRIV_INVALID;
+       }
+       return privs;
+}
+
+AuthManager::AuthManager(const std::string &authfilepath):
+               m_authfilepath(authfilepath)
+{
+       m_mutex.Init();
+       
+       try{
+               load();
+       }
+       catch(SerializationError &e)
+       {
+               dstream<<"WARNING: AuthManager: creating "
+                               <<m_authfilepath<<std::endl;
+       }
+}
+
+AuthManager::~AuthManager()
+{
+       save();
+}
+
+void AuthManager::load()
+{
+       JMutexAutoLock lock(m_mutex);
+       
+       dstream<<"AuthManager: loading from "<<m_authfilepath<<std::endl;
+       std::ifstream is(m_authfilepath.c_str(), std::ios::binary);
+       if(is.good() == false)
+       {
+               dstream<<"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;
+       }
+}
+
+void AuthManager::save()
+{
+       JMutexAutoLock lock(m_mutex);
+       
+       dstream<<"AuthManager: saving to "<<m_authfilepath<<std::endl;
+       std::ofstream os(m_authfilepath.c_str(), std::ios::binary);
+       if(os.good() == false)
+       {
+               dstream<<"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";
+       }
+}
+
+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;
+}
+
+void AuthManager::add(const std::string &username)
+{
+       JMutexAutoLock lock(m_mutex);
+       
+       m_authdata[username] = AuthData();
+}
+
+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);
+}
+
+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);
+}
+
diff --git a/src/auth.h b/src/auth.h
new file mode 100644 (file)
index 0000000..472409d
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+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 <string>
+#include <jthread.h>
+#include <jmutex.h>
+#include "common_irrlicht.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_BUILD = 1;            // Can build - i.e. modify the world
+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
+
+// 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_BUILD|PRIV_SHOUT;
+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::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);
+private:
+       JMutex m_mutex;
+       std::string m_authfilepath;
+       core::map<std::string, AuthData> m_authdata;
+};
+
+#endif
+
index 5869dc77bbab1714d1e12d6395b890811578327a..79bbd8021ca64ba60b2429e67b5ab9a8c4f37c8a 100644 (file)
@@ -610,6 +610,13 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
                // to be processed even if the serialisation format has
                // not been agreed yet, the same as TOCLIENT_INIT.
                m_access_denied = true;
+               m_access_denied_reason = L"Unknown";
+               if(datasize >= 4)
+               {
+                       std::string datastring((char*)&data[2], datasize-2);
+                       std::istringstream is(datastring, std::ios_base::binary);
+                       m_access_denied_reason = deSerializeWideString(is);
+               }
                return;
        }
 
index f661838cee216ce5466d352845558721f41085a4..1bfbe629667172a7d9b231d37e86b5f636204ffb 100644 (file)
@@ -385,6 +385,11 @@ public:
                return m_access_denied;
        }
 
+       inline std::wstring accessDeniedReason()
+       {
+               return m_access_denied_reason;
+       }
+
 private:
        
        // Virtual methods from con::PeerHandler
@@ -440,6 +445,7 @@ private:
        
        std::string m_password;
        bool m_access_denied;
+       std::wstring m_access_denied_reason;
 
        InventoryContext m_inventory_context;
 
index 256aed362779b0da8ae5176359d724d61ff86c68..7972762c05af82eccdbf9be60b0026c530a42fc9 100644 (file)
@@ -24,6 +24,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #define PROTOCOL_ID 0x4f457403
 
+#define PASSWORD_SIZE 28       // Maximum password length. Allows for
+                               // base64-encoded SHA-1 (27+\0).
+
 enum ToClientCommand
 {
        TOCLIENT_INIT = 0x10,
@@ -154,6 +157,8 @@ enum ToClientCommand
        TOCLIENT_ACCESS_DENIED = 0x35,
        /*
                u16 command
+               u16 reason_length
+               wstring reason
        */
 };
 
index f11b0b2d77e337a09d5dd294edca20afe9a1b123..1d758a2a4d2a1e2de70c8d46f8d9d60b80f213bc 100644 (file)
@@ -69,8 +69,10 @@ void set_default_settings()
        // Server stuff
        g_settings.setDefault("enable_experimental", "false");
        g_settings.setDefault("creative_mode", "false");
-       g_settings.setDefault("enable_damage", "false"); //TODO: Set to true
+       g_settings.setDefault("enable_damage", "false"); //TODO: Set to true when healing is possible
        g_settings.setDefault("give_initial_stuff", "false");
+       g_settings.setDefault("default_password", "");
+       g_settings.setDefault("default_privs", "build, shout");
 
        g_settings.setDefault("objectdata_interval", "0.2");
        g_settings.setDefault("active_object_range", "2");
index 3ebfef0c539e185826788e6063efd9314193b39a..c93d11ca8a7314432d68e73c577a646944541bb7 100644 (file)
@@ -426,7 +426,14 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
                        testplayer.deSerialize(is);
                }
 
-               dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
+               if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
+               {
+                       dstream<<"Not loading player with invalid name: "
+                                       <<testplayer.getName()<<std::endl;
+               }
+
+               dstream<<"Loaded test player with name "<<testplayer.getName()
+                               <<std::endl;
                
                // Search for the player
                std::string playername = testplayer.getName();
@@ -723,6 +730,7 @@ void ServerEnvironment::step(float dtime)
                        activateObjects(block);
 
                        // TODO: Do something
+                       // TODO: Implement usage of ActiveBlockModifier
                        
                        // Here's a quick demonstration
                        v3s16 p0;
@@ -784,6 +792,7 @@ void ServerEnvironment::step(float dtime)
                                Everything should bind to inside this single content
                                searching loop to keep things fast.
                        */
+                       // TODO: Implement usage of ActiveBlockModifier
 
                        v3s16 p0;
                        for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)
index ac290f932cac31be4bbec9ec7be14e57a3ac3298..f5cce59339ab01451f8655edf85aeb652f1d19ce 100644 (file)
@@ -112,25 +112,6 @@ public:
 private:
 };
 
-/*
-       Active block modifier interface
-*/
-
-class ServerEnvironment;
-
-class ActiveBlockModifier
-{
-public:
-       ActiveBlockModifier(){};
-       virtual ~ActiveBlockModifier(){};
-       
-       virtual u32 getTriggerContentCount(){ return 1;}
-       virtual u8 getTriggerContent(u32 i) = 0;
-       virtual float getActiveInterval() = 0;
-       virtual u32 getActiveChance() = 0;
-       virtual void triggerEvent(ServerEnvironment *env, v3s16 p) = 0;
-};
-
 /*
        The server-side environment.
 
@@ -140,6 +121,7 @@ public:
 #include "serverobject.h"
 
 class Server;
+class ActiveBlockModifier;
 
 class ServerEnvironment : public Environment
 {
@@ -178,6 +160,7 @@ public:
 
        /*
                ActiveObjects
+               -------------------------------------------
        */
 
        ServerActiveObject* getActiveObject(u16 id);
@@ -214,6 +197,13 @@ public:
        */
        ActiveObjectMessage getActiveObjectMessage();
 
+       /*
+               ActiveBlockModifiers
+               -------------------------------------------
+       */
+
+       void addActiveBlockModifier(ActiveBlockModifier *abm);
+
 private:
        /*
                Remove all objects that satisfy (m_removed && m_known_by_count==0)
@@ -263,6 +253,29 @@ private:
        float m_game_time_fraction_counter;
 };
 
+/*
+       Active block modifier interface.
+
+       These are fed into ServerEnvironment at initialization time;
+       ServerEnvironment handles deleting them.
+*/
+
+class ActiveBlockModifier
+{
+public:
+       ActiveBlockModifier(){};
+       virtual ~ActiveBlockModifier(){};
+
+       //virtual core::list<u8> update(ServerEnvironment *env) = 0;
+       virtual u32 getTriggerContentCount(){ return 1;}
+       virtual u8 getTriggerContent(u32 i) = 0;
+       virtual float getActiveInterval() = 0;
+       // chance of (1 / return value), 0 is disallowed
+       virtual u32 getActiveChance() = 0;
+       // This is called usually at interval for 1/chance of the nodes
+       virtual void triggerEvent(ServerEnvironment *env, v3s16 p) = 0;
+};
+
 #ifndef SERVER
 
 #include "clientobject.h"
index 603a86da3e0b53467d4286f0a0d2bd1a1d9c96cd..cc758be7e8a412e7fe936dd9add3045e9380ad7c 100644 (file)
@@ -764,8 +764,9 @@ void the_game(
        {
                if(client.accessDenied())
                {
-                       error_message = L"Access denied. Check your password and try again.";
-                       std::cout<<DTIME<<"Access denied."<<std::endl;
+                       error_message = L"Access denied. Reason: "
+                                       +client.accessDeniedReason();
+                       std::cout<<DTIME<<wide_to_narrow(error_message)<<std::endl;
                }
                else
                {
index a30e006a63b468065685cd80286178be972fb9fb..ef0a013f14e850403e5be8b65332d0426e01943a 100644 (file)
@@ -266,7 +266,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
                core::rect<s32> rect(0, 0, 130, 30);
                //rect += topleft_server + v2s32(size_server.X-40-130, 100+25);
                rect += topleft_server + v2s32(40, 100+25);
-               Environment->addButton(rect, this, 260, L"Delete map");
+               Environment->addButton(rect, this, 260, L"Delete world");
        }
 }
 
index f67d53475ad234fbc5105dc07a7bca343c471849..11d50bfd1376051a6fb2b6787b1ecea62faf8a81 100644 (file)
@@ -260,6 +260,9 @@ SUGG: MovingObject::move and Player::move are basically the same.
        - NOTE: There is a simple move implementation now in collision.{h,cpp}\r
        - NOTE: MovingObject will be deleted (MapBlockObject)\r
 \r
+TODO: Add a long step function to objects that is called with the time\r
+      difference when block activates\r
+\r
 Map:\r
 ----\r
 \r
@@ -304,14 +307,22 @@ Making it more portable:
  \r
 Stuff to do before release:\r
 ---------------------------\r
-- Player default privileges and default password\r
-- Chat privilege\r
-- Some simple block-based dynamic stuff in the world (finish the\r
-  ActiveBlockModifier stuff)\r
+- Make grass grow slower; utilize timestamp difference\r
 - Protocol version field\r
 - Consider getting some textures from cisoun's texture pack\r
-- Add a long step function to objects that is called with the time\r
-  difference when block activates\r
+       - Ask from Cisoun\r
+- Make sure the fence implementation and data format is good\r
+       - Think about using same bits for material for fences and doors, for\r
+       example\r
+- Make sure server handles removing grass when a block is placed (etc)\r
+    - The client should not do it by itself\r
+- Add mouse inversion in config\r
+- Block cube placement around player's head\r
+- Move mineral to param2, increment map serialization version, add conversion\r
+\r
+Stuff to do after release:\r
+---------------------------\r
+- Finish the ActiveBlockModifier stuff and use it for something\r
 \r
 ======================================================================\r
 \r
@@ -1188,7 +1199,7 @@ int main(int argc, char *argv[])
                port = 30000;\r
        \r
        // Map directory\r
-       std::string map_dir = porting::path_userdata+"/map";\r
+       std::string map_dir = porting::path_userdata+"/world";\r
        if(cmd_args.exists("map-dir"))\r
                map_dir = cmd_args.get("map-dir");\r
        else if(g_settings.exists("map-dir"))\r
@@ -1488,13 +1499,20 @@ int main(int argc, char *argv[])
                                g_settings.set("creative_mode", itos(menudata.creative_mode));\r
                                g_settings.set("enable_damage", itos(menudata.enable_damage));\r
                                \r
-                               // Check for valid parameters, restart menu if invalid.\r
+                               /*// Check for valid parameters, restart menu if invalid.\r
                                if(playername == "")\r
                                {\r
                                        error_message = L"Name required.";\r
                                        continue;\r
                                }\r
-                               \r
+                               // Check that name has only valid chars\r
+                               if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)\r
+                               {\r
+                                       error_message = L"Characters allowed: "\r
+                                                       +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);\r
+                                       continue;\r
+                               }*/\r
+\r
                                // Save settings\r
                                g_settings.set("name", playername);\r
                                g_settings.set("address", address);\r
index 147b6c97acd3f96c390ee761795b1c49c1133f0b..efb2f34473341910e80d647bc0d21fc4a92f3afa 100644 (file)
@@ -23,58 +23,6 @@ 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(privs & PRIV_SHOUT)
-               os<<L"shout,";
-       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 if(*i == L"shout")
-                       privs |= PRIV_SHOUT;
-               else
-                       return PRIV_INVALID;
-       }
-       return privs;
-}
-
 
 Player::Player():
        touching_ground(false),
@@ -83,7 +31,6 @@ Player::Player():
        swimming_up(false),
        craftresult_is_preview(true),
        hp(20),
-       privs(PRIV_DEFAULT),
        peer_id(PEER_ID_INEXISTENT),
        m_pitch(0),
        m_yaw(0),
@@ -91,7 +38,6 @@ Player::Player():
        m_position(0,0,0)
 {
        updateName("<not set>");
-       updatePassword("");
        resetInventory();
 }
 
@@ -150,7 +96,7 @@ void Player::serialize(std::ostream &os)
        Settings args;
        args.setS32("version", 1);
        args.set("name", m_name);
-       args.set("password", m_password);
+       //args.set("password", m_password);
        args.setFloat("pitch", m_pitch);
        args.setFloat("yaw", m_yaw);
        args.setV3F("position", m_position);
@@ -185,10 +131,10 @@ void Player::deSerialize(std::istream &is)
        //args.getS32("version");
        std::string name = args.get("name");
        updateName(name.c_str());
-       std::string password = "";
+       /*std::string password = "";
        if(args.exists("password"))
                password = args.get("password");
-       updatePassword(password.c_str());
+       updatePassword(password.c_str());*/
        m_pitch = args.getFloat("pitch");
        m_yaw = args.getFloat("yaw");
        m_position = args.getV3F("position");
@@ -202,7 +148,7 @@ void Player::deSerialize(std::istream &is)
        }catch(SettingNotFoundException &e){
                hp = 20;
        }
-       try{
+       /*try{
                std::string sprivs = args.get("privs");
                if(sprivs == "all")
                {
@@ -215,7 +161,7 @@ void Player::deSerialize(std::istream &is)
                }
        }catch(SettingNotFoundException &e){
                privs = PRIV_DEFAULT;
-       }
+       }*/
 
        inventory.deSerialize(is);
 }
index 4b776a03fe6a62427a5510157f467c9c2ba7f8ae..157a25b5b073b75a6fb6b484d3d4676c385a175f 100644 (file)
@@ -25,39 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "collision.h"
 
 #define PLAYERNAME_SIZE 20
-#define PASSWORD_SIZE 28       // Maximum password length. Allows for
-                               // base64-encoded SHA-1.
-
-#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
-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
-
-// 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_BUILD|PRIV_SHOUT;
-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);
+
+#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
 
 
 class Map;
@@ -128,16 +97,6 @@ public:
                return m_name;
        }
 
-       virtual void updatePassword(const char *password)
-       {
-               snprintf(m_password, PASSWORD_SIZE, "%s", password);
-       }
-
-       const char * getPassword()
-       {
-               return m_password;
-       }
-
        virtual bool isLocal() const = 0;
 
        virtual void updateLight(u8 light_at_pos) {};
@@ -174,7 +133,6 @@ public:
 
 protected:
        char m_name[PLAYERNAME_SIZE];
-       char m_password[PASSWORD_SIZE];
        f32 m_pitch;
        f32 m_yaw;
        v3f m_speed;
index 9248e62984696caf7f5ba0f305aa9cbddedcecc9..4569d028e7c79addd39b8670b2b22bae59cc90e9 100644 (file)
@@ -967,6 +967,7 @@ Server::Server(
        ):
        m_env(new ServerMap(mapsavedir), this),
        m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
+       m_authmanager(mapsavedir+"/auth.txt"),
        m_thread(this),
        m_emergethread(this),
        m_time_counter(0),
@@ -1646,7 +1647,7 @@ void Server::AsyncRunStep()
                }
        }
 
-       // Save map
+       // Save map, players and auth stuff
        {
                float &counter = m_savemap_timer;
                counter += dtime;
@@ -1654,8 +1655,11 @@ void Server::AsyncRunStep()
                {
                        counter = 0.0;
 
+                       // Auth stuff
+                       m_authmanager.save();
+                       
+                       // Map
                        JMutexAutoLock lock(m_env_mutex);
-
                        if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
                        {
                                // Save only changed parts
@@ -1795,7 +1799,23 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                        playername[i] = data[3+i];
                }
                playername[PLAYERNAME_SIZE-1] = 0;
-       
+               
+               if(playername[0]=='\0')
+               {
+                       derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
+                       SendAccessDenied(m_con, peer_id,
+                                       L"Empty name");
+                       return;
+               }
+
+               if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
+               {
+                       derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
+                       SendAccessDenied(m_con, peer_id,
+                                       L"Name contains unallowed characters");
+                       return;
+               }
+
                // Get password
                char password[PASSWORD_SIZE];
                if(datasize == 2+1+PLAYERNAME_SIZE)
@@ -1811,15 +1831,37 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
                                }
                                password[PASSWORD_SIZE-1] = 0;
                }
-               Player *checkplayer = m_env.getPlayer(playername);
-               if(checkplayer != NULL && strcmp(checkplayer->getPassword(),password))
+               
+               std::string checkpwd;
+               if(m_authmanager.exists(playername))
+               {
+                       checkpwd = m_authmanager.getPassword(playername);
+               }
+               else
+               {
+                       checkpwd = g_settings.get("default_password");
+               }
+               
+               if(password != checkpwd)
                {
                        derr_server<<DTIME<<"Server: peer_id="<<peer_id
                                        <<": supplied invalid password for "
                                        <<playername<<std::endl;
-                       SendAccessDenied(m_con, peer_id);
+                       SendAccessDenied(m_con, peer_id, L"Invalid password");
                        return;
                }
+               
+               // Add player to auth manager
+               if(m_authmanager.exists(playername) == false)
+               {
+                       derr_server<<DTIME<<"Server: adding player "<<playername
+                                       <<" to auth manager"<<std::endl;
+                       m_authmanager.add(playername);
+                       m_authmanager.setPassword(playername, checkpwd);
+                       m_authmanager.setPrivs(playername,
+                                       stringToPrivs(g_settings.get("default_privs")));
+                       m_authmanager.save();
+               }
 
                // Get player
                Player *player = emergePlayer(playername, password, peer_id);
@@ -3020,19 +3062,51 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 
                if(datasize != 2+PASSWORD_SIZE*2)
                        return;
-               char password[PASSWORD_SIZE];
+               /*char password[PASSWORD_SIZE];
                for(u32 i=0; i<PASSWORD_SIZE-1; i++)
                        password[i] = data[2+i];
-               password[PASSWORD_SIZE-1] = 0;
-               if(strcmp(player->getPassword(),password))
+               password[PASSWORD_SIZE-1] = 0;*/
+               std::string oldpwd;
+               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+               {
+                       char c = data[2+i];
+                       if(c == 0)
+                               break;
+                       oldpwd += c;
+               }
+               std::string newpwd;
+               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+               {
+                       char c = data[2+PASSWORD_SIZE+i];
+                       if(c == 0)
+                               break;
+                       newpwd += c;
+               }
+
+               std::string playername = player->getName();
+
+               if(m_authmanager.exists(playername) == false)
                {
+                       dstream<<"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);
+               
+               if(oldpwd != checkpwd)
+               {
+                       dstream<<"Server: invalid old password"<<std::endl;
                        // Wrong old password supplied!!
                        SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
                        return;
                }
-               for(u32 i=0; i<PASSWORD_SIZE-1; i++)
-                       password[i] = data[30+i];
-               player->updatePassword(password);
+
+               m_authmanager.setPassword(playername, newpwd);
+               
+               dstream<<"Server: password change successful for "<<playername
+                               <<std::endl;
                SendChatMessage(peer_id, L"Password change successful");
        }
        else
@@ -3215,12 +3289,14 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
        con.Send(peer_id, 0, data, true);
 }
 
-void Server::SendAccessDenied(con::Connection &con, u16 peer_id)
+void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
+               const std::wstring &reason)
 {
        DSTACK(__FUNCTION_NAME);
        std::ostringstream os(std::ios_base::binary);
 
        writeU16(os, TOCLIENT_ACCESS_DENIED);
+       os<<serializeWideString(reason);
 
        // Make data buffer
        std::string s = os.str();
@@ -4207,7 +4283,10 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
                //player->peer_id = PEER_ID_INEXISTENT;
                player->peer_id = peer_id;
                player->updateName(name);
-               player->updatePassword(password);
+               m_authmanager.add(name);
+               m_authmanager.setPassword(name, password);
+               m_authmanager.setPrivs(name,
+                               stringToPrivs(g_settings.get("default_privs")));
 
                if(g_settings.exists("default_privs"))
                                player->privs = g_settings.getU64("default_privs");
index 54330856c2d541c8d0606ebea6ede4adc852b21e..a6da801be3be47cd56c09a33b934f36bd99df7bb 100644 (file)
@@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "porting.h"
 #include "map.h"
 #include "inventory.h"
+#include "auth.h"
 
 /*
        Some random functions
@@ -438,7 +439,8 @@ private:
        */
        
        static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
-       static void SendAccessDenied(con::Connection &con, u16 peer_id);
+       static void SendAccessDenied(con::Connection &con, u16 peer_id,
+                       const std::wstring &reason);
        
        /*
                Non-static send methods
@@ -514,6 +516,9 @@ private:
        JMutex m_con_mutex;
        // Connected clients (behind the con mutex)
        core::map<u16, RemoteClient*> m_clients;
+
+       // User authentication
+       AuthManager m_authmanager;
        
        /*
                Threads
index 5bb4f67f823d437dff5325e5e6aacd02f1a7e80e..e14f326f40fb28779b9f22b15a611c3bba4cd9fa 100644 (file)
@@ -35,7 +35,7 @@ void cmd_privs(std::wostringstream &os,
        {
                // Show our own real privs, without any adjustments
                // made for admin status
-               os<<L"-!- " + privsToString(ctx->player->privs);
+               os<<L"-!- " + narrow_to_wide(privsToString(ctx->player->privs));
                return;
        }
 
@@ -52,7 +52,7 @@ void cmd_privs(std::wostringstream &os,
                return;
        }
        
-       os<<L"-!- " + privsToString(tp->privs);
+       os<<L"-!- " + narrow_to_wide(privsToString(tp->privs));
 }
 
 void cmd_grantrevoke(std::wostringstream &os,
@@ -70,7 +70,7 @@ void cmd_grantrevoke(std::wostringstream &os,
                return;
        }
 
-       u64 newprivs = stringToPrivs(ctx->parms[2]);
+       u64 newprivs = stringToPrivs(wide_to_narrow(ctx->parms[2]));
        if(newprivs == PRIV_INVALID)
        {
                os<<L"-!- Invalid privileges specified";
@@ -90,7 +90,7 @@ void cmd_grantrevoke(std::wostringstream &os,
                tp->privs &= ~newprivs;
        
        os<<L"-!- Privileges change to ";
-       os<<privsToString(tp->privs);
+       os<<narrow_to_wide(privsToString(tp->privs));
 }
 
 void cmd_time(std::wostringstream &os,
index 254b1f28aade159080df0d2f6eaa26d6e037e2c6..f3b17000c4d9285d93b0604c0e7c400227a00bbc 100644 (file)
@@ -301,7 +301,7 @@ int main(int argc, char *argv[])
        }
        
        // Figure out path to map
-       std::string map_dir = porting::path_userdata+"/map";
+       std::string map_dir = porting::path_userdata+"/world";
        if(cmd_args.exists("map-dir"))
                map_dir = cmd_args.get("map-dir");
        else if(g_settings.exists("map-dir"))
index c7513e94d807a72e0901e89259377fefed3a6ecf..f18d3127841522732f0c514adc47790bca804351 100644 (file)
@@ -1984,17 +1984,23 @@ inline std::string serializeString(const std::string &plain)
        return s;
 }
 
-/*// Reads a string with the length as the first two bytes
-inline std::string deSerializeString(const std::string encoded)
+// Creates a string with the length as the first two bytes from wide string
+inline std::string serializeWideString(const std::wstring &plain)
 {
-       u16 s_size = readU16((u8*)&encoded.c_str()[0]);
-       if(s_size > encoded.length() - 2)
-               return "";
+       //assert(plain.size() <= 65535);
+       if(plain.size() > 65535)
+               throw SerializationError("String too long for serializeString");
+       char buf[2];
+       writeU16((u8*)buf, plain.size());
        std::string s;
-       s.reserve(s_size);
-       s.append(&encoded.c_str()[2], s_size);
+       s.append(buf, 2);
+       for(u32 i=0; i<plain.size(); i++)
+       {
+               writeU16((u8*)buf, plain[i]);
+               s.append(buf, 2);
+       }
        return s;
-}*/
+}
 
 // Reads a string with the length as the first two bytes
 inline std::string deSerializeString(std::istream &is)
@@ -2014,6 +2020,27 @@ inline std::string deSerializeString(std::istream &is)
        return s;
 }
 
+// Reads a wide string with the length as the first two bytes
+inline std::wstring deSerializeWideString(std::istream &is)
+{
+       char buf[2];
+       is.read(buf, 2);
+       if(is.gcount() != 2)
+               throw SerializationError("deSerializeString: size not read");
+       u16 s_size = readU16((u8*)buf);
+       if(s_size == 0)
+               return L"";
+       std::wstring s;
+       s.reserve(s_size);
+       for(u32 i=0; i<s_size; i++)
+       {
+               is.read(&buf[0], 2);
+               wchar_t c16 = readU16((u8*)buf);
+               s.append(&c16, 1);
+       }
+       return s;
+}
+
 // Creates a string with the length as the first four bytes
 inline std::string serializeLongString(const std::string &plain)
 {
@@ -2025,18 +2052,6 @@ inline std::string serializeLongString(const std::string &plain)
        return s;
 }
 
-/*// Reads a string with the length as the first four bytes
-inline std::string deSerializeLongString(const std::string encoded)
-{
-       u32 s_size = readU32((u8*)&encoded.c_str()[0]);
-       if(s_size > encoded.length() - 4)
-               return "";
-       std::string s;
-       s.reserve(s_size);
-       s.append(&encoded.c_str()[4], s_size);
-       return s;
-}*/
-
 // Reads a string with the length as the first four bytes
 inline std::string deSerializeLongString(std::istream &is)
 {