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