]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - include/modules/cap.h
Allow converting a Cap::Reference to a Cap::Capability*.
[user/henk/code/inspircd.git] / include / modules / cap.h
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2018-2019 Sadie Powell <sadie@witchery.services>
5  *   Copyright (C) 2015-2016, 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 "event.h"
24
25 namespace Cap
26 {
27         static const unsigned int MAX_CAPS = (sizeof(intptr_t) * 8) - 1;
28         static const intptr_t CAP_302_BIT = (intptr_t)1 << MAX_CAPS;
29         static const unsigned int MAX_VALUE_LENGTH = 100;
30
31         typedef intptr_t Ext;
32         class ExtItem : public LocalIntExt
33         {
34          public:
35                 ExtItem(Module* mod);
36                 void FromInternal(Extensible* container, const std::string& value) CXX11_OVERRIDE;
37                 std::string ToHuman(const Extensible* container, void* item) const CXX11_OVERRIDE;
38                 std::string ToInternal(const Extensible* container, void* item) const CXX11_OVERRIDE;
39         };
40
41         class Capability;
42
43         enum Protocol
44         {
45                 /** Supports capability negotiation protocol v3.1, or none
46                  */
47                 CAP_LEGACY,
48
49                 /** Supports capability negotiation v3.2
50                  */
51                 CAP_302
52         };
53
54         class EventListener : public Events::ModuleEventListener
55         {
56          public:
57                 EventListener(Module* mod)
58                         : ModuleEventListener(mod, "event/cap")
59                 {
60                 }
61
62                 /** Called whenever a new client capability becomes available or unavailable
63                  * @param cap Capability being added or removed
64                  * @param add If true, the capability is being added, otherwise its being removed
65                  */
66                 virtual void OnCapAddDel(Capability* cap, bool add) = 0;
67
68                 /** Called whenever the value of a cap changes.
69                  * @param cap Capability whose value changed
70                  */
71                 virtual void OnCapValueChange(Capability* cap) { }
72         };
73
74         class Manager : public DataProvider
75         {
76          public:
77                 Manager(Module* mod)
78                         : DataProvider(mod, "capmanager")
79                 {
80                 }
81
82                 /** Register a client capability.
83                  * Modules should call Capability::SetActive(true) instead of this method.
84                  * @param cap Capability to register
85                  */
86                 virtual void AddCap(Capability* cap) = 0;
87
88                 /** Unregister a client capability.
89                  * Modules should call Capability::SetActive(false) instead of this method.
90                  * @param cap Capability to unregister
91                  */
92                 virtual void DelCap(Capability* cap) = 0;
93
94                 /** Find a capability by name
95                  * @param name Capability to find
96                  * @return Capability object pointer if found, NULL otherwise
97                  */
98                 virtual Capability* Find(const std::string& name) const = 0;
99
100                 /** Notify manager when a value of a cap changed
101                  * @param cap Cap whose value changed
102                  */
103                 virtual void NotifyValueChange(Capability* cap) = 0;
104         };
105
106         /** Represents a client capability.
107          *
108          * Capabilities offer extensions to the client to server protocol. They must be negotiated with clients before they have any effect on the protocol.
109          * Each cap must have a unique name that is used during capability negotiation.
110          *
111          * After construction the cap is ready to be used by clients without any further setup, like other InspIRCd services.
112          * The get() method accepts a user as parameter and can be used to check whether that user has negotiated usage of the cap. This is only known for local users.
113          *
114          * The cap module must be loaded for the capability to work. The IsRegistered() method can be used to query whether the cap is actually online or not.
115          * The capability can be deactivated and reactivated with the SetActive() method. Deactivated caps behave as if they don't exist.
116          *
117          * It is possible to implement special behavior by inheriting from this class and overriding some of its methods.
118          */
119         class Capability : public ServiceProvider, private dynamic_reference_base::CaptureHook
120         {
121                 typedef size_t Bit;
122
123                 /** Bit allocated to this cap, undefined if the cap is unregistered
124                  */
125                 Bit bit;
126
127                 /** Extension containing all caps set by a user. NULL if the cap is unregistered.
128                  */
129                 ExtItem* extitem;
130
131                 /** True if the cap is active. Only active caps are registered in the manager.
132                  */
133                 bool active;
134
135                 /** Reference to the cap manager object
136                  */
137                 dynamic_reference<Manager> manager;
138
139                 void OnCapture() CXX11_OVERRIDE
140                 {
141                         if (active)
142                                 SetActive(true);
143                 }
144
145                 void Unregister()
146                 {
147                         bit = 0;
148                         extitem = NULL;
149                 }
150
151                 Ext AddToMask(Ext mask) const { return (mask | GetMask()); }
152                 Ext DelFromMask(Ext mask) const { return (mask & (~GetMask())); }
153                 Bit GetMask() const { return bit; }
154
155                 friend class ManagerImpl;
156
157          protected:
158                 /** Notify the manager that the value of the capability changed.
159                  * Must be called if the value of the cap changes for any reason.
160                  */
161                 void NotifyValueChange()
162                 {
163                         if (IsRegistered())
164                                 manager->NotifyValueChange(this);
165                 }
166
167          public:
168                 /** Constructor, initializes the capability.
169                  * Caps are active by default.
170                  * @param mod Module providing the cap
171                  * @param Name Raw name of the cap as used in the protocol (CAP LS, etc.)
172                  */
173                 Capability(Module* mod, const std::string& Name)
174                         : ServiceProvider(mod, Name, SERVICE_CUSTOM)
175                         , active(true)
176                         , manager(mod, "capmanager")
177                 {
178                         Unregister();
179                 }
180
181                 ~Capability()
182                 {
183                         SetActive(false);
184                 }
185
186                 void RegisterService() CXX11_OVERRIDE
187                 {
188                         manager.SetCaptureHook(this);
189                         SetActive(true);
190                 }
191
192                 /** Check whether a user has the capability turned on.
193                  * This method is safe to call if the cap is unregistered and will return false.
194                  * @param user User to check
195                  * @return True if the user is using this capability, false otherwise
196                  */
197                 bool get(User* user) const
198                 {
199                         if (!IsRegistered())
200                                 return false;
201                         Ext caps = extitem->get(user);
202                         return ((caps & GetMask()) != 0);
203                 }
204
205                 /** Turn the capability on/off for a user. If the cap is not registered this method has no effect.
206                  * @param user User to turn the cap on/off for
207                  * @param val True to turn the cap on, false to turn it off
208                  */
209                 void set(User* user, bool val)
210                 {
211                         if (!IsRegistered())
212                                 return;
213                         Ext curr = extitem->get(user);
214                         extitem->set(user, (val ? AddToMask(curr) : DelFromMask(curr)));
215                 }
216
217                 /** Activate or deactivate the capability.
218                  * If activating, the cap is marked as active and if the manager is available the cap is registered in the manager.
219                  * If deactivating, the cap is marked as inactive and if it is registered, it will be unregistered.
220                  * Users who had the cap turned on will have it turned off automatically.
221                  * @param activate True to activate the cap, false to deactivate it
222                  */
223                 void SetActive(bool activate)
224                 {
225                         active = activate;
226                         if (manager)
227                         {
228                                 if (activate)
229                                         manager->AddCap(this);
230                                 else
231                                         manager->DelCap(this);
232                         }
233                 }
234
235                 /** Get the name of the capability that's used in the protocol
236                  * @return Name of the capability as used in the protocol
237                  */
238                 const std::string& GetName() const { return name; }
239
240                 /** Check whether the capability is active. The cap must be active and registered to be used by users.
241                  * @return True if the cap is active, false if it has been deactivated
242                  */
243                 bool IsActive() const { return active; }
244
245                 /** Check whether the capability is registered
246                  * The cap must be active and the manager must be available for a cap to be registered.
247                  * @return True if the cap is registered in the manager, false otherwise
248                  */
249                 bool IsRegistered() const { return (extitem != NULL); }
250
251                 /** Get the CAP negotiation protocol version of a user.
252                  * The cap must be registered for this to return anything other than CAP_LEGACY.
253                  * @param user User whose negotiation protocol version to query
254                  * @return One of the Capability::Protocol enum indicating the highest supported capability negotiation protocol version
255                  */
256                 Protocol GetProtocol(LocalUser* user) const
257                 {
258                         return ((IsRegistered() && (extitem->get(user) & CAP_302_BIT)) ? CAP_302 : CAP_LEGACY);
259                 }
260
261                 /** Called when a user requests to turn this capability on or off.
262                  * @param user User requesting to change the state of the cap
263                  * @param add True if requesting to turn the cap on, false if requesting to turn it off
264                  * @return True to allow the request, false to reject it
265                  */
266                 virtual bool OnRequest(LocalUser* user, bool add)
267                 {
268                         return true;
269                 }
270
271                 /** Called when a user requests a list of all capabilities and this capability is about to be included in the list.
272                  * The default behavior always includes the cap in the list.
273                  * @param user User querying a list capabilities
274                  * @return True to add this cap to the list sent to the user, false to not list it
275                  */
276                 virtual bool OnList(LocalUser* user)
277                 {
278                         return true;
279                 }
280
281                 /** Query the value of this capability for a user
282                  * @param user User who will get the value of the capability
283                  * @return Value to show to the user. If NULL, the capability has no value (default).
284                  */
285                 virtual const std::string* GetValue(LocalUser* user) const
286                 {
287                         return NULL;
288                 }
289         };
290
291         /** Reference to a cap. The cap may be provided by another module.
292          */
293         class Reference
294         {
295                 dynamic_reference_nocheck<Capability> ref;
296
297          public:
298                 /** Constructor, initializes the capability reference
299                  * @param mod Module creating this object
300                  * @param Name Raw name of the cap as used in the protocol (CAP LS, etc.)
301                  */
302                 Reference(Module* mod, const std::string& Name)
303                         : ref(mod, "cap/" + Name)
304                 {
305                 }
306
307                 /** Retrieves the underlying cap. */
308                 operator const Cap::Capability*() const
309                 {
310                         return ref ? *ref : NULL;
311                 }
312
313                 /** Check whether a user has the referenced capability turned on.
314                  * @param user User to check
315                  * @return True if the user is using the referenced capability, false otherwise
316                  */
317                 bool get(LocalUser* user)
318                 {
319                         if (ref)
320                                 return ref->get(user);
321                         return false;
322                 }
323         };
324
325         class MessageBase : public ClientProtocol::Message
326         {
327          public:
328                 MessageBase(const std::string& subcmd)
329                         : ClientProtocol::Message("CAP", ServerInstance->Config->GetServerName())
330                 {
331                         PushParamPlaceholder();
332                         PushParam(subcmd);
333                 }
334
335                 void SetUser(LocalUser* user)
336                 {
337                         if (user->registered & REG_NICK)
338                                 ReplaceParamRef(0, user->nick);
339                         else
340                                 ReplaceParam(0, "*");
341                 }
342         };
343 }