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