diff options
Diffstat (limited to 'src/database')
-rw-r--r-- | src/database/database-files.cpp | 2 | ||||
-rw-r--r-- | src/database/database-leveldb.cpp | 210 | ||||
-rw-r--r-- | src/database/database-leveldb.h | 32 | ||||
-rw-r--r-- | src/database/database-postgresql.cpp | 179 | ||||
-rw-r--r-- | src/database/database-postgresql.h | 24 | ||||
-rw-r--r-- | src/database/database-sqlite3.cpp | 2 |
6 files changed, 444 insertions, 5 deletions
diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp index d09f1c074..d2b0b1543 100644 --- a/src/database/database-files.cpp +++ b/src/database/database-files.cpp @@ -20,11 +20,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <cassert> #include <json/json.h> #include "database-files.h" -#include "content_sao.h" #include "remoteplayer.h" #include "settings.h" #include "porting.h" #include "filesys.h" +#include "server/player_sao.h" #include "util/string.h" // !!! WARNING !!! diff --git a/src/database/database-leveldb.cpp b/src/database/database-leveldb.cpp index 4a4904c6a..1976ae13d 100644 --- a/src/database/database-leveldb.cpp +++ b/src/database/database-leveldb.cpp @@ -26,6 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "filesys.h" #include "exceptions.h" +#include "remoteplayer.h" +#include "server/player_sao.h" +#include "util/serialize.h" #include "util/string.h" #include "leveldb/db.h" @@ -97,5 +100,210 @@ void Database_LevelDB::listAllLoadableBlocks(std::vector<v3s16> &dst) delete it; } -#endif // USE_LEVELDB +PlayerDatabaseLevelDB::PlayerDatabaseLevelDB(const std::string &savedir) +{ + leveldb::Options options; + options.create_if_missing = true; + leveldb::Status status = leveldb::DB::Open(options, + savedir + DIR_DELIM + "players.db", &m_database); + ENSURE_STATUS_OK(status); +} + +PlayerDatabaseLevelDB::~PlayerDatabaseLevelDB() +{ + delete m_database; +} + +void PlayerDatabaseLevelDB::savePlayer(RemotePlayer *player) +{ + /* + u8 version = 1 + u16 hp + v3f position + f32 pitch + f32 yaw + u16 breath + u32 attribute_count + for each attribute { + std::string name + std::string (long) value + } + std::string (long) serialized_inventory + */ + + std::ostringstream os; + writeU8(os, 1); + + PlayerSAO *sao = player->getPlayerSAO(); + sanity_check(sao); + writeU16(os, sao->getHP()); + writeV3F32(os, sao->getBasePosition()); + writeF32(os, sao->getLookPitch()); + writeF32(os, sao->getRotation().Y); + writeU16(os, sao->getBreath()); + + StringMap stringvars = sao->getMeta().getStrings(); + writeU32(os, stringvars.size()); + for (const auto &it : stringvars) { + os << serializeString(it.first); + os << serializeLongString(it.second); + } + player->inventory.serialize(os); + + leveldb::Status status = m_database->Put(leveldb::WriteOptions(), + player->getName(), os.str()); + ENSURE_STATUS_OK(status); + player->onSuccessfulSave(); +} + +bool PlayerDatabaseLevelDB::removePlayer(const std::string &name) +{ + leveldb::Status s = m_database->Delete(leveldb::WriteOptions(), name); + return s.ok(); +} + +bool PlayerDatabaseLevelDB::loadPlayer(RemotePlayer *player, PlayerSAO *sao) +{ + std::string raw; + leveldb::Status s = m_database->Get(leveldb::ReadOptions(), + player->getName(), &raw); + if (!s.ok()) + return false; + std::istringstream is(raw); + + if (readU8(is) > 1) + return false; + + sao->setHPRaw(readU16(is)); + sao->setBasePosition(readV3F32(is)); + sao->setLookPitch(readF32(is)); + sao->setPlayerYaw(readF32(is)); + sao->setBreath(readU16(is), false); + + u32 attribute_count = readU32(is); + for (u32 i = 0; i < attribute_count; i++) { + std::string name = deSerializeString(is); + std::string value = deSerializeLongString(is); + sao->getMeta().setString(name, value); + } + sao->getMeta().setModified(false); + + // This should always be last. + try { + player->inventory.deSerialize(is); + } catch (SerializationError &e) { + errorstream << "Failed to deserialize player inventory. player_name=" + << player->getName() << " " << e.what() << std::endl; + } + + return true; +} + +void PlayerDatabaseLevelDB::listPlayers(std::vector<std::string> &res) +{ + leveldb::Iterator* it = m_database->NewIterator(leveldb::ReadOptions()); + res.clear(); + for (it->SeekToFirst(); it->Valid(); it->Next()) { + res.push_back(it->key().ToString()); + } + delete it; +} + +AuthDatabaseLevelDB::AuthDatabaseLevelDB(const std::string &savedir) +{ + leveldb::Options options; + options.create_if_missing = true; + leveldb::Status status = leveldb::DB::Open(options, + savedir + DIR_DELIM + "auth.db", &m_database); + ENSURE_STATUS_OK(status); +} + +AuthDatabaseLevelDB::~AuthDatabaseLevelDB() +{ + delete m_database; +} + +bool AuthDatabaseLevelDB::getAuth(const std::string &name, AuthEntry &res) +{ + std::string raw; + leveldb::Status s = m_database->Get(leveldb::ReadOptions(), name, &raw); + if (!s.ok()) + return false; + std::istringstream is(raw); + + /* + u8 version = 1 + std::string password + u16 number of privileges + for each privilege { + std::string privilege + } + s64 last_login + */ + + if (readU8(is) > 1) + return false; + + res.id = 1; + res.name = name; + res.password = deSerializeString(is); + + u16 privilege_count = readU16(is); + res.privileges.clear(); + res.privileges.reserve(privilege_count); + for (u16 i = 0; i < privilege_count; i++) { + res.privileges.push_back(deSerializeString(is)); + } + + res.last_login = readS64(is); + return true; +} + +bool AuthDatabaseLevelDB::saveAuth(const AuthEntry &authEntry) +{ + std::ostringstream os; + writeU8(os, 1); + os << serializeString(authEntry.password); + + size_t privilege_count = authEntry.privileges.size(); + FATAL_ERROR_IF(privilege_count > U16_MAX, + "Unsupported number of privileges"); + writeU16(os, privilege_count); + for (const std::string &privilege : authEntry.privileges) { + os << serializeString(privilege); + } + + writeS64(os, authEntry.last_login); + leveldb::Status s = m_database->Put(leveldb::WriteOptions(), + authEntry.name, os.str()); + return s.ok(); +} + +bool AuthDatabaseLevelDB::createAuth(AuthEntry &authEntry) +{ + return saveAuth(authEntry); +} + +bool AuthDatabaseLevelDB::deleteAuth(const std::string &name) +{ + leveldb::Status s = m_database->Delete(leveldb::WriteOptions(), name); + return s.ok(); +} + +void AuthDatabaseLevelDB::listNames(std::vector<std::string> &res) +{ + leveldb::Iterator* it = m_database->NewIterator(leveldb::ReadOptions()); + res.clear(); + for (it->SeekToFirst(); it->Valid(); it->Next()) { + res.emplace_back(it->key().ToString()); + } + delete it; +} + +void AuthDatabaseLevelDB::reload() +{ + // No-op for LevelDB. +} + +#endif // USE_LEVELDB diff --git a/src/database/database-leveldb.h b/src/database/database-leveldb.h index d30f9f8f5..61def1256 100644 --- a/src/database/database-leveldb.h +++ b/src/database/database-leveldb.h @@ -45,4 +45,36 @@ private: leveldb::DB *m_database; }; +class PlayerDatabaseLevelDB : public PlayerDatabase +{ +public: + PlayerDatabaseLevelDB(const std::string &savedir); + ~PlayerDatabaseLevelDB(); + + void savePlayer(RemotePlayer *player); + bool loadPlayer(RemotePlayer *player, PlayerSAO *sao); + bool removePlayer(const std::string &name); + void listPlayers(std::vector<std::string> &res); + +private: + leveldb::DB *m_database; +}; + +class AuthDatabaseLevelDB : public AuthDatabase +{ +public: + AuthDatabaseLevelDB(const std::string &savedir); + virtual ~AuthDatabaseLevelDB(); + + virtual bool getAuth(const std::string &name, AuthEntry &res); + virtual bool saveAuth(const AuthEntry &authEntry); + virtual bool createAuth(AuthEntry &authEntry); + virtual bool deleteAuth(const std::string &name); + virtual void listNames(std::vector<std::string> &res); + virtual void reload(); + +private: + leveldb::DB *m_database; +}; + #endif // USE_LEVELDB diff --git a/src/database/database-postgresql.cpp b/src/database/database-postgresql.cpp index d7c94ff15..e1bb39928 100644 --- a/src/database/database-postgresql.cpp +++ b/src/database/database-postgresql.cpp @@ -36,8 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "debug.h" #include "exceptions.h" #include "settings.h" -#include "content_sao.h" #include "remoteplayer.h" +#include "server/player_sao.h" Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) : m_connect_string(connect_string) @@ -160,6 +160,11 @@ void Database_PostgreSQL::endSave() checkResults(PQexec(m_conn, "COMMIT;")); } +void Database_PostgreSQL::rollback() +{ + checkResults(PQexec(m_conn, "ROLLBACK;")); +} + MapDatabasePostgreSQL::MapDatabasePostgreSQL(const std::string &connect_string): Database_PostgreSQL(connect_string), MapDatabase() @@ -301,7 +306,7 @@ void MapDatabasePostgreSQL::listAllLoadableBlocks(std::vector<v3s16> &dst) int numrows = PQntuples(results); for (int row = 0; row < numrows; ++row) - dst.push_back(pg_to_v3s16(results, 0, 0)); + dst.push_back(pg_to_v3s16(results, row, 0)); PQclear(results); } @@ -631,4 +636,174 @@ void PlayerDatabasePostgreSQL::listPlayers(std::vector<std::string> &res) PQclear(results); } +AuthDatabasePostgreSQL::AuthDatabasePostgreSQL(const std::string &connect_string) : + Database_PostgreSQL(connect_string), AuthDatabase() +{ + connectToDatabase(); +} + +void AuthDatabasePostgreSQL::createDatabase() +{ + createTableIfNotExists("auth", + "CREATE TABLE auth (" + "id SERIAL," + "name TEXT UNIQUE," + "password TEXT," + "last_login INT NOT NULL DEFAULT 0," + "PRIMARY KEY (id)" + ");"); + + createTableIfNotExists("user_privileges", + "CREATE TABLE user_privileges (" + "id INT," + "privilege TEXT," + "PRIMARY KEY (id, privilege)," + "CONSTRAINT fk_id FOREIGN KEY (id) REFERENCES auth (id) ON DELETE CASCADE" + ");"); +} + +void AuthDatabasePostgreSQL::initStatements() +{ + prepareStatement("auth_read", "SELECT id, name, password, last_login FROM auth WHERE name = $1"); + prepareStatement("auth_write", "UPDATE auth SET name = $1, password = $2, last_login = $3 WHERE id = $4"); + prepareStatement("auth_create", "INSERT INTO auth (name, password, last_login) VALUES ($1, $2, $3) RETURNING id"); + prepareStatement("auth_delete", "DELETE FROM auth WHERE name = $1"); + + prepareStatement("auth_list_names", "SELECT name FROM auth ORDER BY name DESC"); + + prepareStatement("auth_read_privs", "SELECT privilege FROM user_privileges WHERE id = $1"); + prepareStatement("auth_write_privs", "INSERT INTO user_privileges (id, privilege) VALUES ($1, $2)"); + prepareStatement("auth_delete_privs", "DELETE FROM user_privileges WHERE id = $1"); +} + +bool AuthDatabasePostgreSQL::getAuth(const std::string &name, AuthEntry &res) +{ + verifyDatabase(); + + const char *values[] = { name.c_str() }; + PGresult *result = execPrepared("auth_read", 1, values, false, false); + int numrows = PQntuples(result); + if (numrows == 0) { + PQclear(result); + return false; + } + + res.id = pg_to_uint(result, 0, 0); + res.name = std::string(PQgetvalue(result, 0, 1), PQgetlength(result, 0, 1)); + res.password = std::string(PQgetvalue(result, 0, 2), PQgetlength(result, 0, 2)); + res.last_login = pg_to_int(result, 0, 3); + + PQclear(result); + + std::string playerIdStr = itos(res.id); + const char *privsValues[] = { playerIdStr.c_str() }; + PGresult *results = execPrepared("auth_read_privs", 1, privsValues, false); + + numrows = PQntuples(results); + for (int row = 0; row < numrows; row++) + res.privileges.emplace_back(PQgetvalue(results, row, 0)); + + PQclear(results); + + return true; +} + +bool AuthDatabasePostgreSQL::saveAuth(const AuthEntry &authEntry) +{ + verifyDatabase(); + + beginSave(); + + std::string lastLoginStr = itos(authEntry.last_login); + std::string idStr = itos(authEntry.id); + const char *values[] = { + authEntry.name.c_str() , + authEntry.password.c_str(), + lastLoginStr.c_str(), + idStr.c_str(), + }; + execPrepared("auth_write", 4, values); + + writePrivileges(authEntry); + + endSave(); + return true; +} + +bool AuthDatabasePostgreSQL::createAuth(AuthEntry &authEntry) +{ + verifyDatabase(); + + std::string lastLoginStr = itos(authEntry.last_login); + const char *values[] = { + authEntry.name.c_str() , + authEntry.password.c_str(), + lastLoginStr.c_str() + }; + + beginSave(); + + PGresult *result = execPrepared("auth_create", 3, values, false, false); + + int numrows = PQntuples(result); + if (numrows == 0) { + errorstream << "Strange behaviour on auth creation, no ID returned." << std::endl; + PQclear(result); + rollback(); + return false; + } + + authEntry.id = pg_to_uint(result, 0, 0); + PQclear(result); + + writePrivileges(authEntry); + + endSave(); + return true; +} + +bool AuthDatabasePostgreSQL::deleteAuth(const std::string &name) +{ + verifyDatabase(); + + const char *values[] = { name.c_str() }; + execPrepared("auth_delete", 1, values); + + // privileges deleted by foreign key on delete cascade + return true; +} + +void AuthDatabasePostgreSQL::listNames(std::vector<std::string> &res) +{ + verifyDatabase(); + + PGresult *results = execPrepared("auth_list_names", 0, + NULL, NULL, NULL, false, false); + + int numrows = PQntuples(results); + + for (int row = 0; row < numrows; ++row) + res.emplace_back(PQgetvalue(results, row, 0)); + + PQclear(results); +} + +void AuthDatabasePostgreSQL::reload() +{ + // noop for PgSQL +} + +void AuthDatabasePostgreSQL::writePrivileges(const AuthEntry &authEntry) +{ + std::string authIdStr = itos(authEntry.id); + const char *values[] = { authIdStr.c_str() }; + execPrepared("auth_delete_privs", 1, values); + + for (const std::string &privilege : authEntry.privileges) { + const char *values[] = { authIdStr.c_str(), privilege.c_str() }; + execPrepared("auth_write_privs", 2, values); + } +} + + #endif // USE_POSTGRESQL diff --git a/src/database/database-postgresql.h b/src/database/database-postgresql.h index db0b505c9..f47deda33 100644 --- a/src/database/database-postgresql.h +++ b/src/database/database-postgresql.h @@ -34,6 +34,7 @@ public: void beginSave(); void endSave(); + void rollback(); bool initialized() const; @@ -144,3 +145,26 @@ protected: private: bool playerDataExists(const std::string &playername); }; + +class AuthDatabasePostgreSQL : private Database_PostgreSQL, public AuthDatabase +{ +public: + AuthDatabasePostgreSQL(const std::string &connect_string); + virtual ~AuthDatabasePostgreSQL() = default; + + virtual void verifyDatabase() { Database_PostgreSQL::verifyDatabase(); } + + virtual bool getAuth(const std::string &name, AuthEntry &res); + virtual bool saveAuth(const AuthEntry &authEntry); + virtual bool createAuth(AuthEntry &authEntry); + virtual bool deleteAuth(const std::string &name); + virtual void listNames(std::vector<std::string> &res); + virtual void reload(); + +protected: + virtual void createDatabase(); + virtual void initStatements(); + +private: + virtual void writePrivileges(const AuthEntry &authEntry); +}; diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp index 1bacdfe6c..4560743b9 100644 --- a/src/database/database-sqlite3.cpp +++ b/src/database/database-sqlite3.cpp @@ -33,8 +33,8 @@ SQLite format specification: #include "settings.h" #include "porting.h" #include "util/string.h" -#include "content_sao.h" #include "remoteplayer.h" +#include "server/player_sao.h" #include <cassert> |