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