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