1 # ho-mkill.pl
2 #
3 # $Id: ho_mkill.pl,v 1.8 2004/08/22 20:19:26 jvunder REL_0_3 $
4 #
5 # Part of the Hybrid Oper Script Collection.
6 #
7 # This provides a /MKILL command.
8
9 # ---------------------------------------------------------------------
10
11 use strict;
12 use vars qw($VERSION %IRSSI $SCRIPT_NAME);
13
14 use Irssi;
15 use Irssi::Irc;
16 use HOSC::again;
17 use HOSC::again 'HOSC::Base';
18 use HOSC::again 'HOSC::Tools';
19 use Getopt::Long;
20
21 # ---------------------------------------------------------------------
22
23 ($VERSION) = '$Revision: 1.8 $' =~ / (\d+\.\d+) /;
24 %IRSSI = (
25 authors => 'Garion',
26 contact => 'garion@efnet.nl',
27 name => 'ho_mkill.pl',
28 description => 'Masskill command for a channel.',
29 license => 'Public Domain',
30 url => 'http://www.garion.org/irssi/',
31 changed => '12 January 2003 21:53:46',
32 );
33 $SCRIPT_NAME = 'Masskill';
34
35 my $args;
36 my $who_data;
37
38 # ---------------------------------------------------------------------
39
40 sub cmd_mkill {
41 my ($cmdline, $server, $chan) = @_;
42
43 if ($cmdline =~ /^help/) {
44 return print_help();
45 }
46
47 $args = process_arguments($cmdline);
48
49 if ($args->{help}) {
50 return print_help();
51 }
52
53 if (!$server) {
54 ho_print_error("Please use /MKILL from a window of the server you want to masskill on.");
55 return;
56 }
57
58 if (!defined $args->{channel}) {
59 ho_print_error("<channel> argument missing. See /MKILL HELP for help.");
60 return;
61 }
62
63 my $channel = $server->channel_find($args->{channel});
64
65 if (!defined $channel) {
66 ho_print_error("You are not on channel " . $args->{channel} .
67 " on this server.");
68 return;
69 }
70 $args->{channel_obj} = $channel;
71 $args->{server_obj} = $server;
72
73 if (!defined $args->{reason}) {
74 $args->{reason} = Irssi::settings_get_str('ho_mkill_reason');
75 }
76
77 # If we're going for a takeover, we don't need to send a WHO because
78 # we'll kill all other clients anyway.
79 if ($args->{takeover}) {
80 perform_takeover();
81 return;
82 }
83
84 if (defined $args->{hostmask} &&
85 $args->{hostmask} =~ /^(?:(.+)!)?(.+)@(.+)$/
86 ) {
87 $args->{nick} = $1 ? $1 : "*";
88 $args->{user} = $2;
89 $args->{host} = $3;
90 } else {
91 ho_print_error("Missing or invalid hostmask. See /MKILL HELP for help.");
92 return;
93 }
94
95 ho_print('Simulation enabled.') if $args->{simulation};
96 ho_print('Masskilling clients matching ' .
97 $args->{nick} . "!" .
98 $args->{user} . "@" .
99 $args->{host} . " in " .
100 $args->{channel} . " for reason '" . $args->{reason} . "'.");
101
102 # Get rid of all previous WHO data.
103 delete $who_data->{$_} for keys %$who_data;
104
105 # Enable the redirects; otherwise we may end up killing opers.
106 $server->redirect_event('who', 1, $args->{channel}, 0, undef,
107 {
108 'event 352' => 'redir event_who_line',
109 'event 315' => 'redir event_who_end',
110 '' => 'event empty',
111 }
112 );
113 ho_print("Sending WHO.") if $args->{verbose};
114 $server->send_raw_now("WHO " . $args->{channel});
115 }
116
117 # Need to capture who is opered via the output of WHO.
118 sub event_who_line {
119 my ($server, $data, $nick, $address) = @_;
120 my @tokens = split / /, $data;
121 my $nick = $tokens[5];
122 $who_data->{$nick} = {
123 user => $tokens[2],
124 host => $tokens[3],
125 server => $tokens[4],
126 userhost => $tokens[2] . '@' . $tokens[3],
127 };
128
129 $who_data->{$nick}->{opered} = 1 if $tokens[6] =~ /\*/;
130 $who_data->{$nick}->{opped} = 1 if $tokens[6] =~ /@/;
131 Irssi::signal_stop();
132 }
133
134 sub event_who_end {
135 Irssi::signal_stop();
136 perform_masskill();
137 }
138
139 sub perform_takeover {
140 ho_print("Taking over " . $args->{channel});
141 # No need for /WHO data, just use internal irssi channel data.
142 if ($args->{takeover}) {
143 my @nicks = $args->{channel_obj}->nicks();
144 for my $nick (@nicks) {
145 # Don't kill myself!
146 next if $nick->{nick} eq $args->{server_obj}->{nick};
147 my $cmd = "KILL " . $nick->{nick} . " :" . $args->{reason};
148 if ($args->{simulation}) {
149 ho_print($cmd);
150 } else {
151 $args->{server_obj}->send_raw_now($cmd);
152 }
153 }
154 ho_print("Killed " . (scalar @nicks - 1) . " clients.");
155 if ($args->{simulation}) {
156 ho_print("PART " . $args->{channel});
157 ho_print("JOIN " . $args->{channel});
158 } else {
159 $args->{server_obj}->send_raw_now("PART " . $args->{channel});
160 $args->{server_obj}->send_raw_now("JOIN " . $args->{channel});
161 }
162 ho_print("Channel takeover of " . $args->{channel} . " complete.");
163 return;
164 }
165 }
166
167 sub perform_masskill {
168 ho_print("Performing masskill.");
169 my $num_kills = 0;
170 # Normal masskill. Uses WHO #channel data.
171 for my $nick (keys %$who_data) {
172 # Don't kill myself!
173 next if $nick eq $args->{server_obj}->{nick};
174
175 my $hostmask = $who_data->{$nick}->{userhost};
176 if (Irssi::mask_match_address($args->{nick} . '!' .
177 $args->{user} . '@' . $args->{host},
178 $nick, $hostmask)
179 ) {
180 if ($who_data->{$nick}->{opped} && !$args->{ops}) {
181 ho_print($nick . " is opped. Not killing.")
182 if $args->{verbose};
183 next;
184 }
185 if ($who_data->{$nick}->{opered} && !$args->{opers}) {
186 ho_print($nick . " is an oper. Not killing.")
187 if $args->{verbose};
188 next;
189 }
190 my $cmd = "KILL $nick :" . $args->{reason};
191 if ($args->{simulation}) {
192 ho_print($cmd);
193 } else {
194 $args->{server_obj}->send_raw_now($cmd);
195 }
196 $num_kills++;
197 }
198 }
199
200 delete $who_data->{$_} for keys %$who_data;
201
202 ho_print("Done. Killed $num_kills client" .
203 ($num_kills == 1 ? '' : 's') . ".");
204 }
205
206
207 sub process_arguments {
208 my ($arguments) = @_;
209 my $opt;
210
211 local @ARGV = split / /, $arguments;
212
213 my $res = GetOptions(
214 'ops' => \$opt->{ops},
215 'opers' => \$opt->{opers},
216 'takeover' => \$opt->{takeover},
217 'sim' => \$opt->{simulation},
218 'simulate' => \$opt->{simulation},
219 'simulation' => \$opt->{simulation},
220 'verbose' => \$opt->{verbose},
221 );
222
223 # Get the channel.
224 if (@ARGV) {
225 $opt->{channel} = shift @ARGV;
226 }
227
228 # Get the hostmask.
229 if (@ARGV) {
230 $opt->{hostmask} = shift @ARGV;
231 }
232
233 # Reassemble the reason.
234 for my $arg (@ARGV) {
235 $opt->{reason} .= $arg . " ";
236 }
237 $opt->{reason} =~ s/ $// if defined $opt->{reason};
238
239 return $opt;
240 }
241
242 # ---------------------------------------------------------------------
243
244 ho_print_init_begin($SCRIPT_NAME);
245
246 Irssi::command_bind('mkill', 'cmd_mkill');
247 Irssi::settings_add_str('ho', 'ho_mkill_reason', 'Plonk!');
248
249 Irssi::signal_add({ 'redir event_who_line' => \&event_who_line });
250 Irssi::signal_add({ 'redir event_who_end' => \&event_who_end });
251
252 ho_print_init_end($SCRIPT_NAME);
253 ho_print("Use /MKILL HELP for help.");
254
255 # ---------------------------------------------------------------------
256
257 sub print_help {
258 ho_print_help('head', $SCRIPT_NAME);
259
260 ho_print_help('section', 'Syntax');
261 ho_print_help('syntax', 'MKILL [-simulation] [-ops] [-opers] <channel> <[nick!]user@host> [<reason>]');
262 ho_print_help('syntax', 'MKILL [-simulation] -takeover <channel>');
263
264 ho_print_help('section', 'Description');
265 ho_print_help(
266 "Mass kills all users in this channel matching the given hostmask. ".
267 "By default only non-opped users are killed, but if ".
268 "you specify the -ops flag, ops are killed as well.".
269 "Opers are NOT killed by this command. BE CAREFUL if you want ".
270 "to MKILL *\@*; make sure you do a /WHO first because the internal ".
271 "status list might not be up to date with the actual status.\n"
272 );
273 }
syntax highlighted by Code2HTML, v. 0.9.1