2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2018-2020 Sadie Powell <sadie@witchery.services>
5 * Copyright (C) 2015, 2018 Attila Molnar <attilamolnar@hush.com>
7 * This file is part of InspIRCd. InspIRCd is free software: you can
8 * redistribute it and/or modify it under the terms of the GNU General Public
9 * License as published by the Free Software Foundation, version 2.
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 class ModuleEventListener;
28 class ModuleEventProvider;
31 /** Provider of one or more cross-module events.
32 * Modules who wish to provide events for other modules create instances of this class and use
33 * one of the macros below to fire the event, passing the instance of the event provider class
35 * Event providers are identified using a unique identifier string.
37 class Events::ModuleEventProvider : public ServiceProvider, private dynamic_reference_base::CaptureHook
42 bool operator()(ModuleEventListener* lhs, ModuleEventListener* rhs) const;
47 bool operator()(ModuleEventListener* lhs, ModuleEventListener* rhs) const;
50 typedef insp::flat_multiset<ModuleEventListener*, Comp, ElementComp> SubscriberList;
53 * @param mod Module providing the event(s)
54 * @param eventid Identifier of the event or event group provided, must be unique
56 ModuleEventProvider(Module* mod, const std::string& eventid)
57 : ServiceProvider(mod, eventid, SERVICE_DATA)
60 prov.SetCaptureHook(this);
63 /** Retrieves the module which created this listener. */
64 const Module* GetModule() const { return prov.creator; }
66 /** Get list of objects subscribed to this event
67 * @return List of subscribed objects
69 const SubscriberList& GetSubscribers() const { return prov->subscribers; }
71 /** Subscribes a listener to this event.
72 * @param subscriber The listener to subscribe.
74 void Subscribe(ModuleEventListener* subscriber)
76 subscribers.insert(subscriber);
77 OnSubscribe(subscriber);
80 /** Unsubscribes a listener from this event.
81 * @param subscriber The listener to unsubscribe.
83 void Unsubscribe(ModuleEventListener* subscriber)
85 subscribers.erase(subscriber);
86 OnUnsubscribe(subscriber);
90 void OnCapture() CXX11_OVERRIDE
92 // If someone else holds the list from now on, clear mine. See below for more info.
97 /** Called when a listener subscribes to this event.
98 * @param subscriber The listener which subscribed.
100 virtual void OnSubscribe(ModuleEventListener* subscriber) { }
102 /** Called when a listener unsubscribes from this event.
103 * @param subscriber The listener which unsubscribed.
105 virtual void OnUnsubscribe(ModuleEventListener* subscriber) { }
107 /** Reference to the active provider for this event. In case multiple event providers
108 * exist for the same event, only one of them contains the list of subscribers.
109 * To handle the case when we are not the ones with the list, we get it from the provider
110 * where the dynref points to.
112 dynamic_reference_nocheck<ModuleEventProvider> prov;
114 /** List of objects subscribed to the event(s) provided by us, or empty if multiple providers
115 * exist with the same name and we are not the ones holding the list.
117 SubscriberList subscribers;
120 /** Base class for abstract classes describing cross-module events.
121 * Subscribers should NOT inherit directly from this class.
123 class Events::ModuleEventListener : private dynamic_reference_base::CaptureHook
125 /** Reference to the provider, can be NULL if none of the provider modules are loaded
127 dynamic_reference_nocheck<ModuleEventProvider> prov;
129 const unsigned int eventpriority;
131 /** Called by the dynref when the event provider becomes available
133 void OnCapture() CXX11_OVERRIDE
135 prov->Subscribe(this);
139 static const unsigned int DefaultPriority = 100;
142 * @param mod Module subscribing
143 * @param eventid Identifier of the event to subscribe to
144 * @param eventprio The priority to give this event listener
146 ModuleEventListener(Module* mod, const std::string& eventid, unsigned int eventprio = DefaultPriority)
148 , eventpriority(eventprio)
150 prov.SetCaptureHook(this);
151 // If the dynamic_reference resolved at construction our capture handler wasn't called
153 ModuleEventListener::OnCapture();
156 ~ModuleEventListener()
159 prov->Unsubscribe(this);
162 /** Retrieves the module which created this listener. */
163 const Module* GetModule() const { return prov.creator; }
165 /** Retrieves the priority of this event. */
166 unsigned int GetPriority() const { return eventpriority; }
169 inline bool Events::ModuleEventProvider::Comp::operator()(Events::ModuleEventListener* lhs, Events::ModuleEventListener* rhs) const
171 return (lhs->GetPriority() < rhs->GetPriority());
174 inline bool Events::ModuleEventProvider::ElementComp::operator()(Events::ModuleEventListener* lhs, Events::ModuleEventListener* rhs) const
176 if (lhs->GetPriority() < rhs->GetPriority())
178 if (lhs->GetPriority() > rhs->GetPriority())
180 return std::less<ModuleEventListener*>()(lhs, rhs);
184 * Run the given hook provided by a module
186 * FOREACH_MOD_CUSTOM(accountevprov, AccountEventListener, OnAccountChange, MOD_RESULT, (user, newaccount))
188 #define FOREACH_MOD_CUSTOM(prov, listenerclass, func, params) do { \
189 if (!(prov).GetModule() || !(prov).GetModule()->dying) \
191 const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
192 for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
194 listenerclass* _t = static_cast<listenerclass*>(*_i); \
195 const Module* _m = _t->GetModule(); \
196 if (_m && !_m->dying) \
203 * Run the given hook provided by a module until some module returns MOD_RES_ALLOW or MOD_RES_DENY.
204 * If no module does that, result is set to MOD_RES_PASSTHRU.
206 * Example: ModResult MOD_RESULT;
207 * FIRST_MOD_RESULT_CUSTOM(httpevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (request));
209 #define FIRST_MOD_RESULT_CUSTOM(prov, listenerclass, func, result, params) do { \
210 result = MOD_RES_PASSTHRU; \
211 if (!(prov).GetModule() || !(prov).GetModule()->dying) \
213 const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
214 for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
216 listenerclass* _t = static_cast<listenerclass*>(*_i); \
217 const Module* _m = _t->GetModule(); \
218 if (!_m || _m->dying) \
220 result = _t->func params ; \
221 if (result != MOD_RES_PASSTHRU) \