1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
/* +------------------------------------+
* | Inspire Internet Relay Chat Daemon |
* +------------------------------------+
*
* InspIRCd: (C) 2002-2011 InspIRCd Development Team
* See: http://wiki.inspircd.org/Credits
*
* This program is free but copyrighted software; see
* the file COPYING for details.
*
* ---------------------------------------------------
*/
#include "inspircd.h"
#include "protocol.h"
/* $MODDESC: provides commands to clear a given list mode or kick all users */
class ClearBase : public Command
{
public:
ClearBase (Module *me, const std::string &cmd, char flags) : Command (me, cmd, 2, 3)
{
flags_needed = flags;
syntax = "<channel> {users [reason] || +<modeletter> [glob pattern] || <modename> [glob pattern]}";
}
/* this access check returns -1 if user doesn't have access, 0 if he does and if further access checks must be skipped, 1 if user has access to
the command but kick and mode accesses must be checked */
virtual int CheckAccess (User *who, Channel *chan) = 0;
/* function to do things after the clear, like notifying ircops in saclear */
virtual void PostClear (User *user, Channel *chan, const std::string& type)
{
}
/* make the user user clear the mode pointed to by mh on channel chan */
void ListClear (User *user, Channel *chan, ModeHandler *mh, std::string glob, bool accesscheck)
{
/* create the modestacker and fill it with mode changes to be made */
irc::modestacker ms;
const modelist *list = mh->GetList (chan);
const bool matchAll = glob.empty() || (glob == "*");
if (!list)
return;
for (modelist::const_iterator it = list->begin ( ); it != list->end ( ); it++)
if(matchAll || InspIRCd::Match(it->mask, glob))
ms.push (irc::modechange (mh->id, it->mask, false));
/* commit all mode changes and process them, calling handlers and skipping acls, it will update lists */
ServerInstance->Modes->Process (user, chan, ms, false, !accesscheck);
/* then, send information about all mode changes to users in the channel to make clients know it */
ServerInstance->Modes->Send (user, chan, ms);
/* now, send all mode changes to remaining irc servers, that will make servers also process and send modes */
ServerInstance->PI->SendMode (user, chan, ms);
/* write notice */
if (!mh->GetList (chan))
chan->WriteChannel (ServerInstance->FakeClient, "NOTICE %s :%s cleared the %s list mode",
chan->name.c_str(), user->GetFullHost().c_str(), mh->name.c_str());
}
/* command handler */
CmdResult Handle (const std::vector<std::string> ¶ms, User *user)
{
std::string reason;
if (params.size() == 3)
reason.assign(params[2], 0, ServerInstance->Config->Limits.MaxKick);
else
reason = user->nick;
std::string type = params[1];
Channel *chan = ServerInstance->FindChan (params[0]);
std::string channame = params[0];
if (!chan)
{
user->WriteNumeric (ERR_NOSUCHCHANNEL, "%s %s :No such nick/channel", user->nick.c_str ( ), channame.c_str ( ));
return CMD_FAILURE;
}
/* make the access check */
int acc = CheckAccess (user, chan);
if (acc == -1)
return CMD_FAILURE;
/* this depends on what the user wanted to clear */
if (irc::string (type) == "USERS")
{
/* the given user tries to empty the channel */
/* send a notice to the whole channel */
chan->WriteChannel (ServerInstance->FakeClient, "NOTICE %s :This channel is being cleared by %s",
channame.c_str(), user->GetFullHost().c_str());
/* get each channel user and kick him from the channel */
/* if access checks are to be skipped, use FakeClient as source */
User *fuser;
if (acc == 0)
fuser = ServerInstance->FakeClient;
else
fuser = user;
/* kick them */
UserMembList memblist = *chan->GetUsers ( );
for (UserMembList::iterator it = memblist.begin ( ); it != memblist.end ( ); it++)
{
chan->KickUser (fuser, it->first, reason);
}
}
else if (type[0] == '+')
{
/* it is if the user gave a mode letter */
if (type.size() < 2)
{
/* that is called when the user typed + and nothing more, invalid param */
user->WriteServ ("NOTICE %s :You must provide a mode letter for a list mode to clear", user->nick.c_str ( ));
return CMD_FAILURE;
}
/* find the list mode */
ModeHandler *mh = ServerInstance->Modes->FindMode (type[1], MODETYPE_CHANNEL);
if (!mh)
{
user->WriteNumeric (472, "%s %c :is unknown mode char to me", user->nick.c_str ( ), type[1]);
return CMD_FAILURE;
}
else if (!mh->IsListMode ( ))
{
user->WriteServ ("NOTICE %s :the %s mode is not a list mode", user->nick.c_str ( ), mh->name.c_str ( ));
return CMD_FAILURE;
}
/* clear the list mode */
ListClear (user, chan, mh, params.size() == 3 ? params[2] : "", acc == 1);
}
else
{
/* the mode was given as a name */
/* find it */
ModeHandler *mh = ServerInstance->Modes->FindMode (type);
if (!mh)
{
user->WriteNumeric (472, "%s %s :is unknown mode string to me", user->nick.c_str ( ), type.c_str ( ));
return CMD_FAILURE;
}
else if (!mh->IsListMode ( ))
{
user->WriteServ ("NOTICE %s :the %s mode is not a list mode", user->nick.c_str ( ), type.c_str ( ));
return CMD_FAILURE;
}
/* clear the list mode */
ListClear (user, chan, mh, params.size() == 3 ? params[2] : "", acc == 1);
}
PostClear (user, chan, type);
return CMD_SUCCESS;
}
};
class SaclearCommand : public ClearBase
{
public:
SaclearCommand (Module *me) : ClearBase (me, "SACLEAR", 'o')
{
}
/* access checks, really used for server noticing, not for real access checks */
int CheckAccess (User *user, Channel *chan)
{
/* the user has permissions to use this command and doesn't need to be on a channel, so */
/* make it to skip all access checks */
return 0;
}
/* post clear hook */
void PostClear (User *user, Channel *chan, const std::string& type)
{
/* send notice */
ServerInstance->SNO->WriteGlobalSno ('a', "%s used saclear %s on channel %s", user->nick.c_str ( ), type.c_str ( ), chan->name.c_str (
));
}
};
class ClearCommand : public ClearBase
{
public:
ClearCommand (Module *me) : ClearBase (me, "CLEAR", 0)
{
}
int CheckAccess (User *user, Channel *chan)
{
if (!chan->HasUser (user))
{
user->WriteNumeric (ERR_NOTONCHANNEL, "%s %s :you're not on that channel", user->nick.c_str ( ), chan->name.c_str ( ));
return -1;
}
/* allow using the command with access checking */
return 1;
}
};
class ClearModule : public Module
{
SaclearCommand saclearcmd;
ClearCommand clearcmd;
public:
ClearModule ( ) : saclearcmd (this), clearcmd (this)
{
}
Version GetVersion ( )
{
return Version("Provides commands to clear a given list mode or kick all users", VF_VENDOR);
}
void init ( )
{
ServerInstance->Modules->AddService (saclearcmd);
ServerInstance->Modules->AddService (clearcmd);
}
};
MODULE_INIT(ClearModule)
|