]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_clearchan.cpp
Fix the cloaking module on C++98 compilers.
[user/henk/code/inspircd.git] / src / modules / m_clearchan.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2019 Robby <robby@chatbelgie.be>
5  *   Copyright (C) 2017-2018 Sadie Powell <sadie@witchery.services>
6  *   Copyright (C) 2014, 2016 Attila Molnar <attilamolnar@hush.com>
7  *
8  * This file is part of InspIRCd.  InspIRCd is free software: you can
9  * redistribute it and/or modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation, version 2.
11  *
12  * This program is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
15  * details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21
22 #include "inspircd.h"
23 #include "xline.h"
24
25 class CommandClearChan : public Command
26 {
27  public:
28         Channel* activechan;
29
30         CommandClearChan(Module* Creator)
31                 : Command(Creator, "CLEARCHAN", 1, 3)
32         {
33                 syntax = "<channel> [KILL|KICK|G|Z] [:<reason>]";
34                 flags_needed = 'o';
35
36                 // Stop the linking mod from forwarding ENCAP'd CLEARCHAN commands, see below why
37                 force_manual_route = true;
38         }
39
40         CmdResult Handle(User* user, const Params& parameters) CXX11_OVERRIDE
41         {
42                 Channel* chan = activechan = ServerInstance->FindChan(parameters[0]);
43                 if (!chan)
44                 {
45                         user->WriteNotice("The channel " + parameters[0] + " does not exist.");
46                         return CMD_FAILURE;
47                 }
48
49                 // See what method the oper wants to use, default to KILL
50                 std::string method("KILL");
51                 if (parameters.size() > 1)
52                 {
53                         method = parameters[1];
54                         std::transform(method.begin(), method.end(), method.begin(), ::toupper);
55                 }
56
57                 XLineFactory* xlf = NULL;
58                 bool kick = (method == "KICK");
59                 if ((!kick) && (method != "KILL"))
60                 {
61                         if ((method != "Z") && (method != "G"))
62                         {
63                                 user->WriteNotice("Invalid method for clearing " + chan->name);
64                                 return CMD_FAILURE;
65                         }
66
67                         xlf = ServerInstance->XLines->GetFactory(method);
68                         if (!xlf)
69                                 return CMD_FAILURE;
70                 }
71
72                 const std::string reason = parameters.size() > 2 ? parameters.back() : "Clearing " + chan->name;
73
74                 if (!user->server->IsSilentULine())
75                         ServerInstance->SNO->WriteToSnoMask((IS_LOCAL(user) ? 'a' : 'A'), user->nick + " has cleared \002" + chan->name + "\002 (" + method + "): " + reason);
76
77                 user->WriteNotice("Clearing \002" + chan->name + "\002 (" + method + "): " + reason);
78
79                 {
80                         // Route this command manually so it is sent before the QUITs we are about to generate.
81                         // The idea is that by the time our QUITs reach the next hop, it has already removed all their
82                         // clients from the channel, meaning victims on other servers won't see the victims on this
83                         // server quitting.
84                         CommandBase::Params eparams;
85                         eparams.push_back(chan->name);
86                         eparams.push_back(method);
87                         eparams.push_back(":");
88                         eparams.back().append(reason);
89                         ServerInstance->PI->BroadcastEncap(this->name, eparams, user, user);
90                 }
91
92                 // Attach to the appropriate hook so we're able to hide the QUIT/KICK messages
93                 Implementation hook = (kick ? I_OnUserKick : I_OnBuildNeighborList);
94                 ServerInstance->Modules->Attach(hook, creator);
95
96                 std::string mask;
97                 // Now remove all local non-opers from the channel
98                 Channel::MemberMap& users = chan->userlist;
99                 for (Channel::MemberMap::iterator i = users.begin(); i != users.end(); )
100                 {
101                         User* curr = i->first;
102                         const Channel::MemberMap::iterator currit = i;
103                         ++i;
104
105                         if (!IS_LOCAL(curr) || curr->IsOper())
106                                 continue;
107
108                         // If kicking users, remove them and skip the QuitUser()
109                         if (kick)
110                         {
111                                 chan->KickUser(ServerInstance->FakeClient, currit, reason);
112                                 continue;
113                         }
114
115                         // If we are banning users then create the XLine and add it
116                         if (xlf)
117                         {
118                                 XLine* xline;
119                                 try
120                                 {
121                                         mask = ((method[0] == 'Z') ? curr->GetIPString() : "*@" + curr->GetRealHost());
122                                         xline = xlf->Generate(ServerInstance->Time(), 60*60, user->nick, reason, mask);
123                                 }
124                                 catch (ModuleException&)
125                                 {
126                                         // Nothing, move on to the next user
127                                         continue;
128                                 }
129
130                                 if (!ServerInstance->XLines->AddLine(xline, user))
131                                         delete xline;
132                         }
133
134                         ServerInstance->Users->QuitUser(curr, reason);
135                 }
136
137                 ServerInstance->Modules->Detach(hook, creator);
138                 if (xlf)
139                         ServerInstance->XLines->ApplyLines();
140
141                 return CMD_SUCCESS;
142         }
143 };
144
145 class ModuleClearChan : public Module
146 {
147         CommandClearChan cmd;
148
149  public:
150         ModuleClearChan()
151                 : cmd(this)
152         {
153         }
154
155         void init() CXX11_OVERRIDE
156         {
157                 // Only attached while we are working; don't react to events otherwise
158                 ServerInstance->Modules->DetachAll(this);
159         }
160
161         void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE
162         {
163                 bool found = false;
164                 for (IncludeChanList::iterator i = include.begin(); i != include.end(); ++i)
165                 {
166                         if ((*i)->chan == cmd.activechan)
167                         {
168                                 // Don't show the QUIT to anyone in the channel by default
169                                 include.erase(i);
170                                 found = true;
171                                 break;
172                         }
173                 }
174
175                 const Channel::MemberMap& users = cmd.activechan->GetUsers();
176                 for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
177                 {
178                         LocalUser* curr = IS_LOCAL(i->first);
179                         if (!curr)
180                                 continue;
181
182                         if (curr->IsOper())
183                         {
184                                 // If another module has removed the channel we're working on from the list of channels
185                                 // to consider for sending the QUIT to then don't add exceptions for opers, because the
186                                 // module before us doesn't want them to see it or added the exceptions already.
187                                 // If there is a value for this oper in excepts already, this won't overwrite it.
188                                 if (found)
189                                         exception.insert(std::make_pair(curr, true));
190                                 continue;
191                         }
192                         else if (!include.empty() && curr->chans.size() > 1)
193                         {
194                                 // This is a victim and potentially has another common channel with the user quitting,
195                                 // add a negative exception overwriting the previous value, if any.
196                                 exception[curr] = false;
197                         }
198                 }
199         }
200
201         void OnUserKick(User* source, Membership* memb, const std::string& reason, CUList& excepts) CXX11_OVERRIDE
202         {
203                 // Hide the KICK from all non-opers
204                 User* leaving = memb->user;
205                 const Channel::MemberMap& users = memb->chan->GetUsers();
206                 for (Channel::MemberMap::const_iterator i = users.begin(); i != users.end(); ++i)
207                 {
208                         User* curr = i->first;
209                         if ((IS_LOCAL(curr)) && (!curr->IsOper()) && (curr != leaving))
210                                 excepts.insert(curr);
211                 }
212         }
213
214         Version GetVersion() CXX11_OVERRIDE
215         {
216                 return Version("Adds the /CLEARCHAN command which allows server operators to mass-punish the members of a channel.", VF_VENDOR|VF_OPTCOMMON);
217         }
218 };
219
220 MODULE_INIT(ModuleClearChan)