]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_ircv3_capnotify.cpp
Fix the cloaking module on C++98 compilers.
[user/henk/code/inspircd.git] / src / modules / m_ircv3_capnotify.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2018-2020 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 #include "inspircd.h"
22 #include "modules/cap.h"
23 #include "modules/reload.h"
24
25 class CapNotify : public Cap::Capability
26 {
27         bool OnRequest(LocalUser* user, bool add) CXX11_OVERRIDE
28         {
29                 // Users using the negotiation protocol v3.2 or newer may not turn off cap-notify
30                 if ((!add) && (GetProtocol(user) != Cap::CAP_LEGACY))
31                         return false;
32                 return true;
33         }
34
35         bool OnList(LocalUser* user) CXX11_OVERRIDE
36         {
37                 // If the client supports 3.2 enable cap-notify for them
38                 if (GetProtocol(user) != Cap::CAP_LEGACY)
39                         set(user, true);
40                 return true;
41         }
42
43  public:
44         CapNotify(Module* mod)
45                 : Cap::Capability(mod, "cap-notify")
46         {
47         }
48 };
49
50 class CapNotifyMessage : public Cap::MessageBase
51 {
52  public:
53         CapNotifyMessage(bool add, const std::string& capname)
54                 : Cap::MessageBase((add ? "NEW" : "DEL"))
55         {
56                 PushParamRef(capname);
57         }
58 };
59
60 class CapNotifyValueMessage : public Cap::MessageBase
61 {
62         std::string s;
63         const std::string::size_type pos;
64
65  public:
66         CapNotifyValueMessage(const std::string& capname)
67                 : Cap::MessageBase("NEW")
68                 , s(capname)
69                 , pos(s.size()+1)
70         {
71                 s.push_back('=');
72                 PushParamRef(s);
73         }
74
75         void SetCapValue(const std::string& capvalue)
76         {
77                 s.erase(pos);
78                 s.append(capvalue);
79                 InvalidateCache();
80         }
81 };
82
83 class ModuleIRCv3CapNotify : public Module, public Cap::EventListener, public ReloadModule::EventListener
84 {
85         CapNotify capnotify;
86         std::string reloadedmod;
87         std::vector<std::string> reloadedcaps;
88         ClientProtocol::EventProvider protoev;
89
90         void Send(const std::string& capname, Cap::Capability* cap, bool add)
91         {
92                 CapNotifyMessage msg(add, capname);
93                 CapNotifyValueMessage msgwithval(capname);
94
95                 ClientProtocol::Event event(protoev, msg);
96                 ClientProtocol::Event eventwithval(protoev, msgwithval);
97
98                 const UserManager::LocalList& list = ServerInstance->Users.GetLocalUsers();
99                 for (UserManager::LocalList::const_iterator i = list.begin(); i != list.end(); ++i)
100                 {
101                         LocalUser* user = *i;
102                         if (!capnotify.get(user))
103                                 continue;
104
105                         // Check that this user can actually see the cap.
106                         if (add && (!cap || !cap->OnList(user)))
107                                 continue;
108
109                         // If the cap is being added and the client supports cap values then show the value, if any
110                         if ((add) && (capnotify.GetProtocol(user) != Cap::CAP_LEGACY))
111                         {
112                                 const std::string* capvalue = cap->GetValue(user);
113                                 if ((capvalue) && (!capvalue->empty()))
114                                 {
115                                         msgwithval.SetUser(user);
116                                         msgwithval.SetCapValue(*capvalue);
117                                         user->Send(eventwithval);
118                                         continue;
119                                 }
120                         }
121                         msg.SetUser(user);
122                         user->Send(event);
123                 }
124         }
125
126  public:
127         ModuleIRCv3CapNotify()
128                 : Cap::EventListener(this)
129                 , ReloadModule::EventListener(this)
130                 , capnotify(this)
131                 , protoev(this, "CAP_NOTIFY")
132         {
133         }
134
135         void OnCapAddDel(Cap::Capability* cap, bool add) CXX11_OVERRIDE
136         {
137                 if (cap->creator == this)
138                         return;
139
140                 if (cap->creator->ModuleSourceFile == reloadedmod)
141                 {
142                         if (!add)
143                                 reloadedcaps.push_back(cap->GetName());
144                         return;
145                 }
146                 Send(cap->GetName(), cap, add);
147         }
148
149         void OnCapValueChange(Cap::Capability* cap) CXX11_OVERRIDE
150         {
151                 // The value of a cap has changed, send CAP DEL and CAP NEW with the new value
152                 Send(cap->GetName(), cap, false);
153                 Send(cap->GetName(), cap, true);
154         }
155
156         void OnReloadModuleSave(Module* mod, ReloadModule::CustomData& cd) CXX11_OVERRIDE
157         {
158                 if (mod == this)
159                         return;
160                 reloadedmod = mod->ModuleSourceFile;
161                 // Request callback when reload is complete
162                 cd.add(this, NULL);
163         }
164
165         void OnReloadModuleRestore(Module* mod, void* data) CXX11_OVERRIDE
166         {
167                 // Reloading can change the set of caps provided by a module so assuming that if the reload succeeded all
168                 // caps that the module previously provided are available or all were lost if the reload failed is wrong.
169                 // Instead, we verify the availability of each cap individually.
170                 dynamic_reference_nocheck<Cap::Manager> capmanager(this, "capmanager");
171                 if (capmanager)
172                 {
173                         for (std::vector<std::string>::const_iterator i = reloadedcaps.begin(); i != reloadedcaps.end(); ++i)
174                         {
175                                 const std::string& capname = *i;
176                                 if (!capmanager->Find(capname))
177                                         Send(capname, NULL, false);
178                         }
179                 }
180                 reloadedmod.clear();
181                 reloadedcaps.clear();
182         }
183
184         Version GetVersion() CXX11_OVERRIDE
185         {
186                 return Version("Provides the IRCv3 cap-notify client capability.", VF_VENDOR);
187         }
188 };
189
190 MODULE_INIT(ModuleIRCv3CapNotify)