aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPerttu Ahola <celeron55@gmail.com>2011-05-29 21:11:16 +0300
committerPerttu Ahola <celeron55@gmail.com>2011-05-29 21:11:16 +0300
commit63611932ebae93620386b26cfa82f7c4552b22ff (patch)
tree57c065bebf3ba53857dbed80e48cf67cd7e63038
parent99c2ac0125f9bbae0a4b06e6e7145f76f4dfbbf4 (diff)
downloadminetest-63611932ebae93620386b26cfa82f7c4552b22ff.tar.gz
minetest-63611932ebae93620386b26cfa82f7c4552b22ff.tar.bz2
minetest-63611932ebae93620386b26cfa82f7c4552b22ff.zip
player passwords and privileges in world/auth.txt
--HG-- extra : rebase_source : 7260636295d9068fbeeddf4143c89f2b8a91446c
-rw-r--r--.hgignore1
-rw-r--r--doc/changelog.txt1
-rw-r--r--minetest.conf.example5
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/auth.cpp244
-rw-r--r--src/auth.h99
-rw-r--r--src/client.cpp7
-rw-r--r--src/client.h6
-rw-r--r--src/clientserver.h5
-rw-r--r--src/defaultsettings.cpp4
-rw-r--r--src/environment.cpp11
-rw-r--r--src/environment.h51
-rw-r--r--src/game.cpp5
-rw-r--r--src/guiMainMenu.cpp2
-rw-r--r--src/main.cpp36
-rw-r--r--src/player.cpp64
-rw-r--r--src/player.h46
-rw-r--r--src/server.cpp107
-rw-r--r--src/server.h7
-rw-r--r--src/servercommand.cpp8
-rw-r--r--src/servermain.cpp2
-rw-r--r--src/utility.h55
22 files changed, 591 insertions, 176 deletions
diff --git a/.hgignore b/.hgignore
index 58a32bafb..0870e3c67 100644
--- a/.hgignore
+++ b/.hgignore
@@ -1,4 +1,5 @@
map/*
+world/*
CMakeFiles/*
src/CMakeFiles/*
src/Makefile
diff --git a/doc/changelog.txt b/doc/changelog.txt
index f43a68f12..cb3594a98 100644
--- a/doc/changelog.txt
+++ b/doc/changelog.txt
@@ -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
diff --git a/minetest.conf.example b/minetest.conf.example
index acdc19b0a..5e6393228 100644
--- a/minetest.conf.example
+++ b/minetest.conf.example
@@ -102,6 +102,11 @@
#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
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 49982d310..f912f68cd 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -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
index 000000000..49985e697
--- /dev/null
+++ b/src/auth.cpp
@@ -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
index 000000000..472409d46
--- /dev/null
+++ b/src/auth.h
@@ -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
+
diff --git a/src/client.cpp b/src/client.cpp
index 5869dc77b..79bbd8021 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -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;
}
diff --git a/src/client.h b/src/client.h
index f661838ce..1bfbe6296 100644
--- a/src/client.h
+++ b/src/client.h
@@ -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;
diff --git a/src/clientserver.h b/src/clientserver.h
index 256aed362..7972762c0 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -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
*/
};
diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp
index f11b0b2d7..1d758a2a4 100644
--- a/src/defaultsettings.cpp
+++ b/src/defaultsettings.cpp
@@ -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");
diff --git a/src/environment.cpp b/src/environment.cpp
index 3ebfef0c5..c93d11ca8 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -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++)
diff --git a/src/environment.h b/src/environment.h
index ac290f932..f5cce5933 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -113,25 +113,6 @@ 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.
This is not thread-safe. Server uses an environment mutex.
@@ -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"
diff --git a/src/game.cpp b/src/game.cpp
index 603a86da3..cc758be7e 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -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
{
diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp
index a30e006a6..ef0a013f1 100644
--- a/src/guiMainMenu.cpp
+++ b/src/guiMainMenu.cpp
@@ -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");
}
}
diff --git a/src/main.cpp b/src/main.cpp
index f67d53475..11d50bfd1 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -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}
- NOTE: MovingObject will be deleted (MapBlockObject)
+TODO: Add a long step function to objects that is called with the time
+ difference when block activates
+
Map:
----
@@ -304,14 +307,22 @@ Making it more portable:
Stuff to do before release:
---------------------------
-- Player default privileges and default password
-- Chat privilege
-- Some simple block-based dynamic stuff in the world (finish the
- ActiveBlockModifier stuff)
+- Make grass grow slower; utilize timestamp difference
- Protocol version field
- Consider getting some textures from cisoun's texture pack
-- Add a long step function to objects that is called with the time
- difference when block activates
+ - Ask from Cisoun
+- Make sure the fence implementation and data format is good
+ - Think about using same bits for material for fences and doors, for
+ example
+- Make sure server handles removing grass when a block is placed (etc)
+ - The client should not do it by itself
+- Add mouse inversion in config
+- Block cube placement around player's head
+- Move mineral to param2, increment map serialization version, add conversion
+
+Stuff to do after release:
+---------------------------
+- Finish the ActiveBlockModifier stuff and use it for something
======================================================================
@@ -1188,7 +1199,7 @@ int main(int argc, char *argv[])
port = 30000;
// Map directory
- 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"))
@@ -1488,13 +1499,20 @@ int main(int argc, char *argv[])
g_settings.set("creative_mode", itos(menudata.creative_mode));
g_settings.set("enable_damage", itos(menudata.enable_damage));
- // Check for valid parameters, restart menu if invalid.
+ /*// Check for valid parameters, restart menu if invalid.
if(playername == "")
{
error_message = L"Name required.";
continue;
}
-
+ // Check that name has only valid chars
+ if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
+ {
+ error_message = L"Characters allowed: "
+ +narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);
+ continue;
+ }*/
+
// Save settings
g_settings.set("name", playername);
g_settings.set("address", address);
diff --git a/src/player.cpp b/src/player.cpp
index 147b6c97a..efb2f3447 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -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);
}
diff --git a/src/player.h b/src/player.h
index 4b776a03f..157a25b5b 100644
--- a/src/player.h
+++ b/src/player.h
@@ -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;
diff --git a/src/server.cpp b/src/server.cpp
index 9248e6298..4569d028e 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -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");
diff --git a/src/server.h b/src/server.h
index 54330856c..a6da801be 100644
--- a/src/server.h
+++ b/src/server.h
@@ -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
diff --git a/src/servercommand.cpp b/src/servercommand.cpp
index 5bb4f67f8..e14f326f4 100644
--- a/src/servercommand.cpp
+++ b/src/servercommand.cpp
@@ -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,
diff --git a/src/servermain.cpp b/src/servermain.cpp
index 254b1f28a..f3b17000c 100644
--- a/src/servermain.cpp
+++ b/src/servermain.cpp
@@ -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"))
diff --git a/src/utility.h b/src/utility.h
index c7513e94d..f18d31278 100644
--- a/src/utility.h
+++ b/src/utility.h
@@ -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)
{