]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/modules/m_clearchan.cpp
Allow Timers to delete themselves in Tick()
[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(const std::vector<std::string>& parameters, User* user)
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                         std::vector<std::string> 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                 const UserMembList* users = chan->GetUsers();
97                 for (UserMembCIter i = users->begin(); i != users->end(); )
98                 {
99                         User* curr = i->first;
100                         ++i;
101
102                         if (!IS_LOCAL(curr) || curr->IsOper())
103                                 continue;
104
105                         // If kicking users, remove them and skip the QuitUser()
106                         if (kick)
107                         {
108                                 chan->KickUser(ServerInstance->FakeClient, curr, reason);
109                                 continue;
110                         }
111
112                         // If we are banning users then create the XLine and add it
113                         if (xlf)
114                         {
115                                 XLine* xline;
116                                 try
117                                 {
118                                         mask = ((method[0] == 'Z') ? curr->GetIPString() : "*@" + curr->host);
119                                         xline = xlf->Generate(ServerInstance->Time(), 60*60, user->nick, reason, mask);
120                                 }
121                                 catch (ModuleException& ex)
122                                 {
123                                         // Nothing, move on to the next user
124                                         continue;
125                                 }
126
127                                 if (!ServerInstance->XLines->AddLine(xline, user))
128                                         delete xline;
129                         }
130
131                         ServerInstance->Users->QuitUser(curr, reason);
132                 }
133
134                 ServerInstance->Modules->Detach(hook, creator);
135                 if (xlf)
136                         ServerInstance->XLines->ApplyLines();
137
138                 return CMD_SUCCESS;
139         }
140 };
141
142 class ModuleClearChan : public Module
143 {
144         CommandClearChan cmd;
145
146  public:
147         ModuleClearChan()
148                 : cmd(this)
149         {
150         }
151
152         void init()
153         {
154                 // Only attached while we are working; don't react to events otherwise
155                 ServerInstance->Modules->DetachAll(this);
156         }
157
158         void OnBuildNeighborList(User* source, IncludeChanList& include, std::map<User*, bool>& exception) CXX11_OVERRIDE
159         {
160                 bool found = false;
161                 for (IncludeChanList::iterator i = include.begin(); i != include.end(); ++i)
162                 {
163                         if ((*i)->chan == cmd.activechan)
164                         {
165                                 // Don't show the QUIT to anyone in the channel by default
166                                 include.erase(i);
167                                 found = true;
168                                 break;
169                         }
170                 }
171
172                 const UserMembList* users = cmd.activechan->GetUsers();
173                 for (UserMembCIter i = users->begin(); i != users->end(); ++i)
174                 {
175                         LocalUser* curr = IS_LOCAL(i->first);
176                         if (!curr)
177                                 continue;
178
179                         if (curr->IsOper())
180                         {
181                                 // If another module has removed the channel we're working on from the list of channels
182                                 // to consider for sending the QUIT to then don't add exceptions for opers, because the
183                                 // module before us doesn't want them to see it or added the exceptions already.
184                                 // If there is a value for this oper in excepts already, this won't overwrite it.
185                                 if (found)
186                                         exception.insert(std::make_pair(curr, true));
187                                 continue;
188                         }
189                         else if (!include.empty() && curr->chans.size() > 1)
190                         {
191                                 // This is a victim and potentially has another common channel with the user quitting,
192                                 // add a negative exception overwriting the previous value, if any.
193                                 exception[curr] = false;
194                         }
195                 }
196         }
197
198         void OnUserKick(User* source, Membership* memb, const std::string& reason, CUList& excepts) CXX11_OVERRIDE
199         {
200                 // Hide the KICK from all non-opers
201                 User* leaving = memb->user;
202                 const UserMembList* users = memb->chan->GetUsers();
203                 for (UserMembCIter i = users->begin(); i != users->end(); ++i)
204                 {
205                         User* curr = i->first;
206                         if ((IS_LOCAL(curr)) && (!curr->IsOper()) && (curr != leaving))
207                                 excepts.insert(curr);
208                 }
209         }
210
211         Version GetVersion() CXX11_OVERRIDE
212         {
213                 return Version("Adds /CLEARCHAN that allows opers to masskick, masskill or mass-G/ZLine users on a channel", VF_VENDOR|VF_OPTCOMMON);
214         }
215 };
216
217 MODULE_INIT(ModuleClearChan)