From d0f802e30c492cb1b7e55f51063bfd38b29931c6 Mon Sep 17 00:00:00 2001 From: Peter Powell Date: Mon, 13 May 2019 17:24:25 +0100 Subject: Implement serialisation of users. This allows for various things which will be coming in the future. e.g. Transferring users to another server on upgrade. --- src/serializable.cpp | 282 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 src/serializable.cpp (limited to 'src/serializable.cpp') diff --git a/src/serializable.cpp b/src/serializable.cpp new file mode 100644 index 000000000..1d8a794d3 --- /dev/null +++ b/src/serializable.cpp @@ -0,0 +1,282 @@ +/* + * InspIRCd -- Internet Relay Chat Daemon + * + * Copyright (C) 2019 Peter Powell + * + * This file is part of InspIRCd. InspIRCd 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, version 2. + * + * 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, see . + */ + + +#include "inspircd.h" + +Serializable::Data& Serializable::Data::Load(const std::string& key, std::string& out) +{ + EntryMap::iterator iter = this->entries.find(key); + if (iter == this->entries.end()) + { + ServerInstance->Logs->Log("SERIALIZE", LOG_DEBUG, "Unable to load missing kv %s!", key.c_str()); + } + else + { + out = iter->second; + ServerInstance->Logs->Log("SERIALIZE", LOG_DEBUG, "Loaded kv %s: %s", key.c_str(), out.c_str()); + } + return *this; +} + +Serializable::Data& Serializable::Data::Load(const std::string& key, Serializable::Data& out) +{ + ChildMap::iterator iter = this->children.find(key); + if (iter == this->children.end()) + { + ServerInstance->Logs->Log("SERIALIZE", LOG_DEBUG, "Unable to load missing data %s!", key.c_str()); + } + else + { + out = iter->second; + ServerInstance->Logs->Log("SERIALIZE", LOG_DEBUG, "Loaded data: %s", key.c_str()); + } + return *this; +} + +Serializable::Data& Serializable::Data::Store(const std::string& key, const std::string& value) +{ + ServerInstance->Logs->Log("SERIALIZE", LOG_DEBUG, "Stored kv %s: %s", key.c_str(), value.c_str()); + this->entries[key] = value; + return *this; +} + +Serializable::Data& Serializable::Data::Store(const std::string& key, const Serializable::Data& value) +{ + ServerInstance->Logs->Log("SERIALIZE", LOG_DEBUG, "Stored data: %s", key.c_str()); + this->children[key] = value; + return *this; +} + +bool Extensible::Deserialize(Serializable::Data& data) +{ + // If the extensible has been culled then it shouldn't be deserialized. + if (culled) + return false; + + const Serializable::Data::EntryMap& entries = data.GetEntries(); + for (Serializable::Data::EntryMap::const_iterator iter = entries.begin(); iter != entries.end(); ++iter) + { + const std::string& name = iter->first; + ExtensionItem* item = ServerInstance->Extensions.GetItem(name); + if (item) + { + item->FromInternal(this, iter->second); + continue; + } + + ServerInstance->Logs->Log("SERIALIZE", LOG_DEBUG, "Tried to deserialize the %s extension item but it doesn't exist", + name.c_str()); + } + return true; +} + +bool Extensible::Serialize(Serializable::Data& data) +{ + // If the extensible has been culled then it shouldn't be serialized. + if (culled) + { + ServerInstance->Logs->Log("SERIALIZE", LOG_DEBUG, "Tried to serialize an extensible which has been culled"); + return false; + } + + for (Extensible::ExtensibleStore::const_iterator iter = extensions.begin(); iter != extensions.end(); ++iter) + { + ExtensionItem* item = iter->first; + const std::string value = item->ToInternal(this, iter->second); + if (!value.empty()) + data.Store(item->name, value); + } + return true; +} + +bool User::Deserialize(Serializable::Data& data) +{ + // If the user is quitting they shouldn't be deserialized. + if (quitting) + { + ServerInstance->Logs->Log("SERIALIZE", LOG_DEBUG, "Tried to deserialize %s who is in the process of quitting", + uuid.c_str()); + return false; + } + + // Check we're actually deserialising data for this user. + std::string client_uuid; + data.Load("uuid", client_uuid); + if (!client_uuid.empty() && client_uuid != uuid) + { + ServerInstance->Logs->Log("SERIALIZE", LOG_DEBUG, "Tried to deserialize %s into %s", + client_uuid.c_str(), uuid.c_str()); + return false; + } + + // Deserialize the extensions first. + Serializable::Data extensions; + data.Load("extensions", extensions); + if (!Extensible::Deserialize(extensions)) + return false; + + long client_port; + std::string client_addr; + std::string user_modes; + std::string user_oper; + std::string user_snomasks; + + // Apply the members which can be applied directly. + data.Load("age", age) + .Load("awaymsg", awaymsg) + .Load("awaytime", awaytime) + .Load("client_sa.addr", client_addr) + .Load("client_sa.port", client_port) + .Load("displayhost", displayhost) + .Load("ident", ident) + .Load("modes", user_modes) + .Load("nick", nick) + .Load("oper", user_oper) + .Load("realhost", realhost) + .Load("realname", realname) + .Load("signon", signon) + .Load("snomasks", user_snomasks); + + // Apply the rest of the members. + modes = std::bitset(user_modes); + snomasks = std::bitset<64>(user_snomasks); + + ServerConfig::OperIndex::const_iterator iter = ServerInstance->Config->OperTypes.find(user_oper); + if (iter != ServerInstance->Config->OperTypes.end()) + oper = iter->second; + else + oper = new OperInfo(user_oper); + + irc::sockets::sockaddrs sa; + if (irc::sockets::aptosa(client_addr, client_port, sa) || irc::sockets::untosa(client_addr, sa)) + client_sa = sa; + + InvalidateCache(); + return true; +} + +bool User::Serialize(Serializable::Data& data) +{ + // If the user is quitting they shouldn't be serialized. + if (quitting) + { + ServerInstance->Logs->Log("SERIALIZE", LOG_DEBUG, "Tried to serialize %s who is in the process of quitting", + uuid.c_str()); + return false; + } + + // If the user is unregistered they shouldn't be serialised. + if (registered != REG_ALL) + return false; + + // Serialize the extensions first. + Serializable::Data extensions; + if (!Extensible::Serialize(extensions)) + return false; + data.Store("extensions", extensions); + + // The following member variables not checked above are not serialised: + // * cached_fullhost (serialising cache variables is unnecessary) + // * cached_fullrealhost (serialising cache variables is unnecessary) + // * cached_hostip (serialising cache variables is unnecessary) + // * cached_makehost (serialising cache variables is unnecessary) + // * cachedip (serialising cache variables is unnecessary) + // * server (specific to the origin server) + // * usertype (can't be networked reliably) + data.Store("age", age) + .Store("awaymsg", awaymsg) + .Store("awaytime", awaytime) + .Store("client_sa.addr", client_sa.addr()) + .Store("client_sa.port", client_sa.port()) + .Store("displayhost", displayhost) + .Store("ident", ident) + .Store("modes", modes.to_string()) + .Store("nick", nick) + .Store("oper", oper ? oper->name : "") + .Store("realhost", realhost) + .Store("realname", realname) + .Store("signon", signon) + .Store("snomasks", snomasks.to_string()) + .Store("uuid", uuid); + + return true; +} + +bool LocalUser::Deserialize(Serializable::Data& data) +{ + + // Deserialize the base class first. + if (!User::Deserialize(data)) + return false; + + bool user_exempt; + bool user_lastping; + long server_port; + std::string server_addr; + + // Apply the members which can be applied directly. + data.Load("bytes_in", bytes_in) + .Load("bytes_out", bytes_out) + .Load("cmds_in", cmds_in) + .Load("cmds_out", cmds_out) + .Load("CommandFloodPenalty", CommandFloodPenalty) + .Load("exempt", user_exempt) + .Load("idle_lastmsg", idle_lastmsg) + .Load("lastping", user_lastping) + .Load("nextping", nextping) + .Load("password", password) + .Load("server_sa.addr", server_addr) + .Load("server_sa.port", server_port); + + // Apply the rest of the members. + irc::sockets::sockaddrs sa; + if (irc::sockets::aptosa(server_addr, server_port, sa) || irc::sockets::untosa(server_addr, sa)) + server_sa = sa; + + // These are bitfields so we need to ensure they only get the appropriate bits. + exempt = user_exempt ? 1 : 0; + lastping = user_lastping ? 1 : 0; + return true; +} + +bool LocalUser::Serialize(Serializable::Data& data) +{ + // Serialize the base class first. + if (!User::Serialize(data)) + return false; + + // The following member variables not checked above are not serialised: + // * already_sent (can't be networked reliably) + // * eh (shouldn't be networked) + // * MyClass (might not be the same on a different server) + // * serializer (might not be the same on a different connection) + data.Store("bytes_in", bytes_in) + .Store("bytes_out", bytes_out) + .Store("cmds_in", cmds_in) + .Store("cmds_out", cmds_out) + .Store("CommandFloodPenalty", CommandFloodPenalty) + .Store("exempt", exempt) + .Store("idle_lastmsg", idle_lastmsg) + .Store("lastping", lastping) + .Store("nextping", nextping) + .Store("password", password) + .Store("server_sa.addr", server_sa.addr()) + .Store("server_sa.port", server_sa.port()); + return true; +} -- cgit v1.2.3