/* * InspIRCd -- Internet Relay Chat Daemon * * Copyright (C) 2013, 2017-2020 Sadie Powell * Copyright (C) 2012, 2014-2015 Attila Molnar * Copyright (C) 2012 Robby * Copyright (C) 2009 Daniel De Graaf * * 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 . */ #pragma once /** Types of extensible that an extension can extend. */ enum class ExtensionType : uint8_t { /** The extension extends the User class. */ USER = 0, /** The extension extends the Channel class. */ CHANNEL = 1, /** The extension extends the Membership class. */ MEMBERSHIP = 2, }; /** Base class for types that extend an extensible. */ class CoreExport ExtensionItem : public ServiceProvider { public: /** The type of extensible that this extension extends. */ const ExtensionType type; /** Deletes a \p value which is set on \p container. * @param container The container that this extension is set on. * @param item The item to delete. */ virtual void Delete(Extensible* container, void* item) = 0; /** Deserialises a value for this extension of the specified container from the internal format. * @param container A container this extension should be set on. * @param value A value in the internal format. */ virtual void FromInternal(Extensible* container, const std::string& value) noexcept; /** Deserialises a value for this extension of the specified container from the network format. * @param container A container this extension should be set on. * @param value A value in the network format. */ virtual void FromNetwork(Extensible* container, const std::string& value) noexcept; /** @copydoc ServiceProvider::RegisterService */ void RegisterService() override; /** Serialises a value for this extension of the specified container to the human-readable * format. * @param container The container that this extension is set on. * @param item The value to convert to the human-readable format. * @return The value specified in \p item in the human-readable format. */ virtual std::string ToHuman(const Extensible* container, void* item) const noexcept; /** Serialises a value for this extension of the specified container to the internal format. * @param container The container that this extension is set on. * @param item The value to convert to the internal format. * @return The value specified in \p item in the internal format. */ virtual std::string ToInternal(const Extensible* container, void* item) const noexcept; /** Serialises a value for this extension of the specified container to the network format. * @param container The container that this extension is set on. * @param item The value to convert to the network format. * @return The value specified in \p item in the network format. */ virtual std::string ToNetwork(const Extensible* container, void* item) const noexcept; protected: /** Initializes an instance of the ExtensionItem class. * @param owner The module which created the extension. * @param key The name of the extension (e.g. foo-bar). * @param exttype The type of extensible that the extension applies to. */ ExtensionItem(Module* owner, const std::string& key, ExtensionType exttype); /** Retrieves the value for this extension of the specified container from the internal map. * @param container The container that this extension is set on. * @return Either the value of this extension or nullptr if it does not exist. */ void* GetRaw(const Extensible* container) const; /** Sets a value for this extension of the specified container in the internal map and * returns the old value if one was set * @param container The container that this extension should be set on. * @param value The new value to set for this extension. Will NOT be copied. * @return Either the old value or nullptr if one is not set. */ void* SetRaw(Extensible* container, void* value); /** Syncs the value of this extension of the specified container across the network. Does * nothing if an inheritor does not implement ExtensionItem::ToNetwork. * @param container The container that this extension is set on. * @param item The value of this extension. */ void Sync(const Extensible* container, void* item); /** Removes this extension from the specified container and returns it. * @param container The container that this extension should be removed from. * @return Either the old value of this extension or nullptr if it was not set. */ void* UnsetRaw(Extensible* container); }; /** Base class for types which can be extended with additional data. */ class CoreExport Extensible : public Cullable , public Serializable { public: /** The container which extension values are stored in. */ typedef insp::flat_map ExtensibleStore; /** Allows extensions to access the extension store. */ friend class ExtensionItem; ~Extensible() override; /** @copydoc Cullable::Cull */ Cullable::Result Cull() override; /** @copydoc Serializable::Deserialize */ bool Deserialize(Data& data) override; /** Frees all extensions attached to this extensible. */ void FreeAllExtItems(); /** Retrieves the values for extensions which are set on this extensible. */ const ExtensibleStore& GetExtList() const { return extensions; } /** @copydoc Serializable::Deserialize */ bool Serialize(Serializable::Data& data) override; /** Unhooks the specifies extensions from this extensible. * @param items The items to unhook. */ void UnhookExtensions(const std::vector& items); protected: Extensible(); private: /** The values for extensions which are set on this extensible. */ ExtensibleStore extensions; /** Whether this extensible has been culled yet. */ bool culled:1; }; /** Manager for the extension system */ class CoreExport ExtensionManager final { public: /** The container which registered extensions are stored in. */ typedef std::map ExtMap; /** Begins unregistering extensions belonging to the specified module. * @param module The module to unregister extensions for. * @param list The list to add unregistered extensions to. */ void BeginUnregister(Module* module, std::vector& list); /** Retrieves registered extensions keyed by their names. */ const ExtMap& GetExts() const { return types; } /** Retrieves an extension by name. * @param name The name of the extension to retrieve. * @return Either the value of this extension or nullptr if it does not exist. */ ExtensionItem* GetItem(const std::string& name); /** Registers an extension with the manager. * @return Either true if the extension was registered or false if an extension with the same * name already exists. */ bool Register(ExtensionItem* item); private: /** Registered extensions keyed by their names. */ ExtMap types; }; /** An extension which has a simple (usually POD) value. */ template > class SimpleExtItem : public ExtensionItem { protected: /** Whether to sync this extension across the network. */ bool synced; public: /** Initializes an instance of the SimpleExtItem class. * @param owner The module which created the extension. * @param key The name of the extension (e.g. foo-bar). * @param exttype The type of extensible that the extension applies to. * @param sync Whether this extension should be broadcast to other servers. */ SimpleExtItem(Module* owner, const std::string& key, ExtensionType exttype, bool sync = false) : ExtensionItem(owner, key, exttype) , synced(sync) { } /** @copydoc ExtensionItem::Delete */ void Delete(Extensible* container, void* item) override { Del del; del(static_cast(item)); } /** Retrieves the value for this extension of the specified container. * @param container The container that this extension is set on. * @return Either the value of this extension or nullptr if it is not set. */ inline T* Get(const Extensible* container) const { return static_cast(GetRaw(container)); } /** Sets a value for this extension of the specified container. * @param container The container that this extension should be set on. * @param value The new value to set for this extension. Will NOT be copied. * @param sync If syncable then whether to sync this set to the network. */ inline void Set(Extensible* container, T* value, bool sync = true) { T* old = static_cast(SetRaw(container, value)); Delete(container, old); if (sync && synced) Sync(container, value); } /** Sets a value for this extension of the specified container. * @param container The container that this extension should be set on. * @param value The new value to set for this extension. Will be copied. * @param sync If syncable then whether to sync this set to the network. */ inline void Set(Extensible* container, const T& value, bool sync = true) { Set(container, new T(value), sync); } /** Sets a forwarded value for this extension of the specified container. * @param container The container that this extension should be set on. * @param args The arguments to forward to the constructor of \p T. */ template inline void SetFwd(Extensible* container, Args&&... args) { // Forwarded arguments are for complex types which are assumed to not // be synced across the network. You can manually call Sync() if this // is not the case. Set(container, new T(std::forward(args)...), false); } /** Removes this extension from the specified container. * @param container The container that this extension should be removed from. * @param sync If syncable then whether to sync this unset to the network. */ inline void Unset(Extensible* container, bool sync = true) { Delete(container, UnsetRaw(container)); if (synced && sync) Sync(container, nullptr); } }; /** An extension which has a string value. */ class CoreExport StringExtItem : public SimpleExtItem { public: /** Initializes an instance of the StringExtItem class. * @param owner The module which created the extension. * @param key The name of the extension (e.g. foo-bar). * @param exttype The type of extensible that the extension applies to. * @param sync Whether this extension should be broadcast to other servers. */ StringExtItem(Module* owner, const std::string& key, ExtensionType exttype, bool sync = false); /** @copydoc ExtensionItem::FromInternal */ void FromInternal(Extensible* container, const std::string& value) noexcept override; /** @copydoc ExtensionItem::FromNetwork */ void FromNetwork(Extensible* container, const std::string& value) noexcept override; /** @copydoc ExtensionItem::ToInternal */ std::string ToInternal(const Extensible* container, void* item) const noexcept override; /** @copydoc ExtensionItem::ToNetwork */ std::string ToNetwork(const Extensible* container, void* item) const noexcept override; }; /** An extension which has an integer value. */ class CoreExport IntExtItem : public ExtensionItem { protected: /** Whether to sync this extension across the network. */ bool synced; public: /** Initializes an instance of the IntExtItem class. * @param owner The module which created the extension. * @param key The name of the extension (e.g. foo-bar). * @param exttype The type of extensible that the extension applies to. * @param sync Whether this extension should be broadcast to other servers. */ IntExtItem(Module* owner, const std::string& key, ExtensionType exttype, bool sync = false); /** @copydoc ExtensionItem::Delete */ void Delete(Extensible* container, void* item) override; /** Retrieves the value for this extension of the specified container. * @param container The container that this extension is set on. * @return Either the value of this extension or 0 if it is not set. */ intptr_t Get(const Extensible* container) const; /** @copydoc ExtensionItem::FromInternal */ void FromInternal(Extensible* container, const std::string& value) noexcept override; /** @copydoc ExtensionItem::FromNetwork */ void FromNetwork(Extensible* container, const std::string& value) noexcept override; /** Sets a value for this extension of the specified container. * @param container The container that this extension should be set on. * @param value The new value to set for this extension. * @param sync If syncable then whether to sync this set to the network. */ void Set(Extensible* container, intptr_t value, bool sync = true); /** @copydoc ExtensionItem::ToInternal */ std::string ToInternal(const Extensible* container, void* item) const noexcept override; /** @copydoc ExtensionItem::ToNetwork */ std::string ToNetwork(const Extensible* container, void* item) const noexcept override; /** Removes this extension from the specified container. * @param container The container that this extension should be removed from. * @param sync If syncable then whether to sync this unset to the network. */ void Unset(Extensible* container, bool sync = true); }; /** An extension which has a boolean value. */ class CoreExport BoolExtItem : public ExtensionItem { protected: /** Whether to sync this extension across the network. */ bool synced; public: /** Initializes an instance of the BoolExtItem class. * @param owner The module which created the extension. * @param key The name of the extension (e.g. foo-bar). * @param exttype The type of extensible that the extension applies to. * @param sync Whether this extension should be broadcast to other servers. */ BoolExtItem(Module* owner, const std::string& key, ExtensionType exttype, bool sync = false); /** @copydoc ExtensionItem::Delete */ void Delete(Extensible* container, void* item) override; /** Retrieves the value for this extension of the specified container. * @param container The container that this extension is set on. * @return Either the value of this extension or false if it is not set. */ bool Get(const Extensible* container) const; /** @copydoc ExtensionItem::FromInternal */ void FromInternal(Extensible* container, const std::string& value) noexcept override; /** @copydoc ExtensionItem::FromNetwork */ void FromNetwork(Extensible* container, const std::string& value) noexcept override; /** Sets a value for this extension of the specified container. * @param container The container that this extension should be set on. * @param sync If syncable then whether to sync this set to the network. */ void Set(Extensible* container, bool sync = true); /** @copydoc ExtensionItem::ToHuman */ std::string ToHuman(const Extensible* container, void* item) const noexcept override; /** @copydoc ExtensionItem::ToInternal */ std::string ToInternal(const Extensible* container, void* item) const noexcept override; /** @copydoc ExtensionItem::ToNetwork */ std::string ToNetwork(const Extensible* container, void* item) const noexcept override; /** Removes this extension from the specified container. * @param container The container that this extension should be removed from. * @param sync If syncable then whether to sync this unset to the network. */ void Unset(Extensible* container, bool sync = true); };