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