]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/event.h
Sync helpop chmodes s and p with docs
[user/henk/code/inspircd.git] / include / event.h
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2018-2021 Sadie Powell <sadie@witchery.services>
5  *   Copyright (C) 2015, 2018 Attila Molnar <attilamolnar@hush.com>
6  *
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.
10  *
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
14  * details.
15  *
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/>.
18  */
19
20
21 #pragma once
22
23 #include "base.h"
24
25 namespace Events
26 {
27         class ModuleEventListener;
28         class ModuleEventProvider;
29 }
30
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
34  * to the macro.
35  * Event providers are identified using a unique identifier string.
36  */
37 class Events::ModuleEventProvider : public ServiceProvider, private dynamic_reference_base::CaptureHook
38 {
39  public:
40         struct Comp
41         {
42                 bool operator()(ModuleEventListener* lhs, ModuleEventListener* rhs) const;
43         };
44
45         struct ElementComp
46         {
47                 bool operator()(ModuleEventListener* lhs, ModuleEventListener* rhs) const;
48         };
49
50         typedef insp::flat_multiset<ModuleEventListener*, Comp, ElementComp> SubscriberList;
51
52         /** Constructor
53          * @param mod Module providing the event(s)
54          * @param eventid Identifier of the event or event group provided, must be unique
55          */
56         ModuleEventProvider(Module* mod, const std::string& eventid)
57                 : ServiceProvider(mod, eventid, SERVICE_DATA)
58                 , prov(mod, eventid)
59         {
60                 prov.SetCaptureHook(this);
61         }
62
63         /** Retrieves the module which created this listener. */
64         const Module* GetModule() const { return prov.creator; }
65
66         /** Get list of objects subscribed to this event
67          * @return List of subscribed objects
68          */
69         const SubscriberList& GetSubscribers() const { return prov->subscribers; }
70
71         /** Subscribes a listener to this event.
72          * @param subscriber The listener to subscribe.
73          */
74         void Subscribe(ModuleEventListener* subscriber)
75         {
76                 subscribers.insert(subscriber);
77                 OnSubscribe(subscriber);
78         }
79
80         /** Unsubscribes a listener from this event.
81          * @param subscriber The listener to unsubscribe.
82          */
83         void Unsubscribe(ModuleEventListener* subscriber)
84         {
85                 subscribers.erase(subscriber);
86                 OnUnsubscribe(subscriber);
87         }
88
89  private:
90         void OnCapture() CXX11_OVERRIDE
91         {
92                 // If someone else holds the list from now on, clear mine. See below for more info.
93                 if (*prov != this)
94                         subscribers.clear();
95         }
96
97         /** Called when a listener subscribes to this event.
98          * @param subscriber The listener which subscribed.
99          */
100         virtual void OnSubscribe(ModuleEventListener* subscriber) { }
101
102         /** Called when a listener unsubscribes from this event.
103          * @param subscriber The listener which unsubscribed.
104          */
105         virtual void OnUnsubscribe(ModuleEventListener* subscriber) { }
106
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.
111          */
112         dynamic_reference_nocheck<ModuleEventProvider> prov;
113
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.
116          */
117         SubscriberList subscribers;
118 };
119
120 /** Base class for abstract classes describing cross-module events.
121  * Subscribers should NOT inherit directly from this class.
122  */
123 class Events::ModuleEventListener : private dynamic_reference_base::CaptureHook
124 {
125         /** Reference to the provider, can be NULL if none of the provider modules are loaded
126          */
127         dynamic_reference_nocheck<ModuleEventProvider> prov;
128
129         const unsigned int eventpriority;
130
131         /** Called by the dynref when the event provider becomes available
132          */
133         void OnCapture() CXX11_OVERRIDE
134         {
135                 prov->Subscribe(this);
136         }
137
138  public:
139         static const unsigned int DefaultPriority = 100;
140
141         /** Constructor
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
145          */
146         ModuleEventListener(Module* mod, const std::string& eventid, unsigned int eventprio = DefaultPriority)
147                 : prov(mod, eventid)
148                 , eventpriority(eventprio)
149         {
150                 prov.SetCaptureHook(this);
151                 // If the dynamic_reference resolved at construction our capture handler wasn't called
152                 if (prov)
153                         ModuleEventListener::OnCapture();
154         }
155
156         ~ModuleEventListener()
157         {
158                 if (prov)
159                         prov->Unsubscribe(this);
160         }
161
162         /** Retrieves the module which created this listener. */
163         const Module* GetModule() const { return prov.creator; }
164
165         /** Retrieves the priority of this event. */
166         unsigned int GetPriority() const { return eventpriority; }
167 };
168
169 inline bool Events::ModuleEventProvider::Comp::operator()(Events::ModuleEventListener* lhs, Events::ModuleEventListener* rhs) const
170 {
171         return (lhs->GetPriority() < rhs->GetPriority());
172 }
173
174 inline bool Events::ModuleEventProvider::ElementComp::operator()(Events::ModuleEventListener* lhs, Events::ModuleEventListener* rhs) const
175 {
176         if (lhs->GetPriority() < rhs->GetPriority())
177                 return true;
178         if (lhs->GetPriority() > rhs->GetPriority())
179                 return false;
180         return std::less<ModuleEventListener*>()(lhs, rhs);
181 }
182
183 /**
184  * Run the given hook provided by a module
185  *
186  * FOREACH_MOD_CUSTOM(accountevprov, AccountEventListener, OnAccountChange, MOD_RESULT, (user, newaccount))
187  */
188 #define FOREACH_MOD_CUSTOM(prov, listenerclass, func, params) do { \
189         if (!(prov).GetModule() || !(prov).GetModule()->dying) \
190         { \
191                 const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
192                 for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
193                 { \
194                         listenerclass* _t = static_cast<listenerclass*>(*_i); \
195                         const Module* _m = _t->GetModule(); \
196                         if (_m && !_m->dying) \
197                         _t->func params ; \
198                 } \
199         } \
200 } while (0);
201
202 /**
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.
205  *
206  * Example: ModResult MOD_RESULT;
207  * FIRST_MOD_RESULT_CUSTOM(httpevprov, HTTPRequestEventListener, OnHTTPRequest, MOD_RESULT, (request));
208  */
209 #define FIRST_MOD_RESULT_CUSTOM(prov, listenerclass, func, result, params) do { \
210         result = MOD_RES_PASSTHRU; \
211         if (!(prov).GetModule() || !(prov).GetModule()->dying) \
212         { \
213                 const ::Events::ModuleEventProvider::SubscriberList& _handlers = (prov).GetSubscribers(); \
214                 for (::Events::ModuleEventProvider::SubscriberList::const_iterator _i = _handlers.begin(); _i != _handlers.end(); ++_i) \
215                 { \
216                         listenerclass* _t = static_cast<listenerclass*>(*_i); \
217                         const Module* _m = _t->GetModule(); \
218                         if (!_m || _m->dying) \
219                                 continue; \
220                         result = _t->func params ; \
221                         if (result != MOD_RES_PASSTHRU) \
222                                 break; \
223                 } \
224         } \
225 } while (0);