summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Powell <petpow@saberuk.com>2019-05-13 17:24:25 +0100
committerPeter Powell <petpow@saberuk.com>2019-10-14 11:03:03 +0100
commitd0f802e30c492cb1b7e55f51063bfd38b29931c6 (patch)
treea11cbb5c48fae151d285e0536d4f51b2b09659da
parent51b9b4c9b404bd801be194644133be47bd035b58 (diff)
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.
-rw-r--r--include/extensible.h10
-rw-r--r--include/inspircd.h1
-rw-r--r--include/serialize.h117
-rw-r--r--include/users.h14
-rw-r--r--src/serializable.cpp282
-rw-r--r--src/users.cpp9
6 files changed, 432 insertions, 1 deletions
diff --git a/include/extensible.h b/include/extensible.h
index c24984f26..f3eeabdcf 100644
--- a/include/extensible.h
+++ b/include/extensible.h
@@ -133,7 +133,9 @@ class CoreExport ExtensionItem : public ServiceProvider, public usecountbase
* a flags variable, and each module defining bits within the flag as 'theirs' as it is less prone to conflict and
* supports arbitary data storage).
*/
-class CoreExport Extensible : public classbase
+class CoreExport Extensible
+ : public classbase
+ , public Serializable
{
public:
typedef insp::flat_map<reference<ExtensionItem>, void*> ExtensibleStore;
@@ -165,6 +167,12 @@ class CoreExport Extensible : public classbase
* Free all extension items attached to this Extensible
*/
void FreeAllExtItems();
+
+ /** @copydoc Serializable::Deserialize. */
+ bool Deserialize(Data& data) CXX11_OVERRIDE;
+
+ /** @copydoc Serializable::Deserialize. */
+ bool Serialize(Serializable::Data& data) CXX11_OVERRIDE;
};
class CoreExport ExtensionManager
diff --git a/include/inspircd.h b/include/inspircd.h
index 35183c3bf..775c201f9 100644
--- a/include/inspircd.h
+++ b/include/inspircd.h
@@ -74,6 +74,7 @@ struct fakederef
#include "dynref.h"
#include "consolecolors.h"
#include "cull_list.h"
+#include "serialize.h"
#include "extensible.h"
#include "fileutils.h"
#include "ctables.h"
diff --git a/include/serialize.h b/include/serialize.h
new file mode 100644
index 000000000..b3783a48f
--- /dev/null
+++ b/include/serialize.h
@@ -0,0 +1,117 @@
+/*
+ * InspIRCd -- Internet Relay Chat Daemon
+ *
+ * Copyright (C) 2019 Peter Powell <petpow@saberuk.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#pragma once
+
+/** Base class for serializable elements. */
+class CoreExport Serializable
+{
+ protected:
+ Serializable() { }
+
+ public:
+ /** Encapsulates a chunk of serialised data. */
+ class CoreExport Data
+ {
+ public:
+ /** Maps keys to serialised data. */
+ typedef TR1NS::unordered_map<std::string, Data> ChildMap;
+
+ /** Maps keys to simple values. */
+ typedef TR1NS::unordered_map<std::string, std::string> EntryMap;
+
+ private:
+ /** A mapping of keys to serialised data. */
+ ChildMap children;
+
+ /** A mapping of keys to values. */
+ EntryMap entries;
+
+ public:
+ /** Retrieves the child elements. */
+ const ChildMap& GetChildren() const { return children; }
+ ChildMap& GetChildren() { return children; }
+
+ /** Retrieves the key/value map. */
+ const EntryMap& GetEntries() const { return entries; }
+ EntryMap& GetEntries() { return entries; }
+
+ /** Loads the serialised data with the specified key.
+ * @param key The key by which this serialised data is identified.
+ * @param out The location to store the serialised data for this key.
+ */
+ Data& Load(const std::string& key, Data& out);
+
+ /** Loads the value with the specified key.
+ * @param key The key by which this data is identified.
+ * @param out The location to store the value for this keu
+ */
+ Data& Load(const std::string& key, std::string& out);
+
+ /** Loads the value with the specified key. The value will be converted to the specified type.
+ * @param key The key by which this data is identified.
+ * @param out The location to store the value for this key.
+ */
+ template <typename T>
+ Data& Load(const std::string& key, T& out)
+ {
+ // Attempt to load as a string.
+ std::string str;
+ Load(key, str);
+
+ std::stringstream ss(str);
+ ss >> out;
+ return *this;
+ }
+
+ /** Stores the serialised data against the specified key.
+ * @param key The key by which this serialised data should be stored against.
+ * @param out The serialised data to store.
+ */
+ Data& Store(const std::string& key, const Data& value);
+
+ /** Stores the value against the specified key.
+ * @param key The key by which this value should be stored against.
+ * @param out The value to store.
+ */
+ Data& Store(const std::string& key, const std::string& value);
+
+ /** Stores the value against the specified key. The value will be converted to a string using ConvToStr.
+ * @param key The key by which this value should be stored against.
+ * @param out The value to store.
+ */
+ template <typename T>
+ Data& Store(const std::string& key, const T& value)
+ {
+ return Store(key, ConvToStr(value));
+ }
+ };
+
+ /** Deserializes the specified Data instance into this object.
+ * @param data The Data object to deserialize from.
+ * @return True if the deserialisation succeeded; otherwise, false.
+ */
+ virtual bool Deserialize(Data& data) = 0;
+
+ /** Serializes the this object into the specified Data obect.
+ * @param data The Data object to serialize to.
+ * @return True if the serialisation succeeded; otherwise, false.
+ */
+ virtual bool Serialize(Data& data) = 0;
+};
diff --git a/include/users.h b/include/users.h
index e58e8e316..6cc7f423b 100644
--- a/include/users.h
+++ b/include/users.h
@@ -676,6 +676,12 @@ class CoreExport User : public Extensible
*/
virtual ~User();
CullResult cull() CXX11_OVERRIDE;
+
+ /** @copydoc Serializable::Deserialize. */
+ bool Deserialize(Data& data) CXX11_OVERRIDE;
+
+ /** @copydoc Serializable::Deserialize. */
+ bool Serialize(Serializable::Data& data) CXX11_OVERRIDE;
};
class CoreExport UserIOHandler : public StreamSocket
@@ -729,6 +735,8 @@ class CoreExport LocalUser : public User, public insp::intrusive_list_node<Local
public:
LocalUser(int fd, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server);
+ LocalUser(int fd, const std::string& uuid, Serializable::Data& data);
+
CullResult cull() CXX11_OVERRIDE;
UserIOHandler eh;
@@ -872,6 +880,12 @@ class CoreExport LocalUser : public User, public insp::intrusive_list_node<Local
* @param msg Message to send.
*/
void Send(ClientProtocol::EventProvider& protoevprov, ClientProtocol::Message& msg);
+
+ /** @copydoc Serializable::Deserialize. */
+ bool Deserialize(Data& data) CXX11_OVERRIDE;
+
+ /** @copydoc Serializable::Deserialize. */
+ bool Serialize(Serializable::Data& data) CXX11_OVERRIDE;
};
class RemoteUser : public User
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 <petpow@saberuk.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+#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<ModeParser::MODEID_MAX>(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;
+}
diff --git a/src/users.cpp b/src/users.cpp
index b8a2d31a8..01bad8b61 100644
--- a/src/users.cpp
+++ b/src/users.cpp
@@ -112,6 +112,15 @@ LocalUser::LocalUser(int myfd, irc::sockets::sockaddrs* client, irc::sockets::so
ChangeRealHost(GetIPString(), true);
}
+LocalUser::LocalUser(int myfd, const std::string& uuid, Serializable::Data& data)
+ : User(uuid, ServerInstance->FakeClient->server, USERTYPE_LOCAL)
+ , eh(this)
+ , already_sent(0)
+{
+ eh.SetFd(myfd);
+ Deserialize(data);
+}
+
User::~User()
{
}