#!/usr/bin/perl -w #use strict; use Digest::MD5 qw(md5 md5_hex md5_base64); use Irssi qw(command_bind); use Irssi::Irc; $VERSION = "0.8"; %IRSSI = ( authors => 'CrazyEddy', contact => 'crazyed@epicsol.org', name => 'ceop', description => 'An Irssi implementation of the ceop cryptographically secure auto-op system.', license => 'GNU GPL, but this is negotiable.', url => 'http://www.epicsol.org/~crazyed/cetk.ceop.pl', changed => 'Wed Jul 21 03:13:10 EST 2004', changes => 'Initial release.', ); # # This is a companion irssi script for the cryptographically secure auto-op # system found in the cetk.opme epic script which explains how the protocol # works and what it is useful for. Both can be found at # www.epicsol.org/~crazyed/ . # # Since the best documentation for this system is in the epic script named # above, only a few "quick start" examples will be given here: # # /ceop.setkey testkey . # /ceop.autoop # # If there are any opped clients on the current channel that use that key, then # by default they will op you, and your client will op them if they know the # key. The best thing is that you don't have to worry about anybody cracking # the key because it will never actually be transmitted on the network. # # This means that you don't have to have a more elaborate bot on the channel # because this script is simple enough for a group of people to use. # # In this version, settings can only be stored by defining an /alias that # configures them. This is not a particularly good way to do business, and # will probably change in the future. # # /ceop.alias is used to define extensions and it can be a little tricky to # use. The body of the alias is a perl expression, and a few perl variables # are available: # # $args is the remainding argument portion of the ceop request. # $nick is the nick of the message sender. # $uh is the user@host of the message sender. # $dest is the destination of the request. # $chan is the channel that the message is refering to. # $Chan is the Irssi object version of $chan. # $Serv is the Irssi object of the server the request was received on. # # To add an Irssi extension, follow one of these examples: # # /ceop.alias test 0 $Serv->command("msg $nick Testing.") # /ceop.alias test 0 $Chan->command("msg $chan Testing.") # # The ultimate dangerous but useful extensions of course are these: # # /ceop.alias perl 1000 eval $args # /ceop.alias cmd 1000 $Chan->command("$args") # /ceop.alias eval 1000 $Chan->command("eval $args") # /ceop.alias exec 1000 $Chan->command("exec -msg $nick $args") # sub sig_ctcp_op { my ($Serv, $args, $nick, $uh, $dest) = @_; my $Win = Irssi::active_win(); my $md5 = $args =~ s/^\s*(\S+)\s*// ? $1 : ""; my $user = $md5 =~ s/.*[\/,]// ? $& : ""; my $ARGS = $args; my $chan = $args =~ s/^\s*([^\w\s]\S+)\s*// ? $1 : $dest; my $ts = $args =~ s/^\s*(\d+)\s*// ? $1 : ""; my $cmd = $args =~ s/^\s*(\w+)\s*// ? $1 : ""; my $clev = Irssi::settings_get_int( $ceopcmds{$cmd} ? "ceop_command_level_$cmd" : "ceop_default_command_level"); my $Chan = $Serv->channel_find($chan) || return; my $isop = $Chan->nick_find($Serv->{nick})->{op}; my @bans = map { $_->{ban} } $Chan->bans(); my @bans = grep { $Serv->masks_match($_,$nick,$uh) } @bans; my $onchan = $Chan->nick_find($nick); for $key (keys %ceopkeys) { next if $user ne $ceopkeys{$key}{user}; next if $md5 ne md5_hex("$nick!$uh $key $dest $ARGS"); next if $chan !~ /$ceopkeys{$key}{chan}/i; next if $ts && time - $ts > $ceopkeys{$key}{age}; if ($ceopkeys{$key}{level} < $clev) { Irssi::print "CEOP command \"$cmd\" restricted."; } elsif ($cmd eq "") { if (!$isop) { cmd_ceop_autoop($chan, $Serv, $Win); } elsif (@bans) { $Chan->command("unban @bans"); } elsif (!$onchan) { $Chan->command("invite $nick"); } else { $Chan->command("op $nick"); } } elsif ($cmd eq "clobber") { Irssi::print("Clobbered ceop key $key"); $ceopkeys{$key}{level} = -1; } elsif ($cmd eq "help") { $Serv->command("notice $nick Available commands: " .join " ", sort keys %ceopcmds); } elsif (defined $ceopcmds{$cmd}) { eval $ceopcmds{$cmd}; } return; } } # # Someone tell me how to get irssi to actually _call_ subcommands _without_ # having to define a base command and I will put these all under /ceop. # sub cmd_ceop_request { my ($args, $serv, $win, $trash) = @_; if (!$serv || !$serv->{connected}) { Irssi::print("Not connected to server"); } elsif (!$serv->{userhost}) { Irssi::print("Not joined to a channel"); } else { my $nuh = $serv->{nick} ."!". $serv->{userhost}; my $key = $args =~ s/^\s*(\S+)\s*// ? $1 : ""; my $dest = $args =~ s/^\s*(\S+)\s*// ? $1 : $win->{name}; my $md5 = md5_hex("$nuh $key $dest $args"); $serv->command("ctcp $dest op $md5 $args"); } }; sub cmd_ceop_autoop { my ($args, $serv, $win, $trash) = @_; if (!$serv || !$serv->{connected}) { Irssi::print("Not connected to server"); } elsif (!$serv->{userhost}) { Irssi::print("Not joined to a channel"); } else { my ($dest,$chan) = $args =~ /\s*(\S+)\s*(\S+)/; my $dest = $dest ? $dest : $win->{name}; my $chan = $chan ? $chan : $dest; for (keys %ceopkeys) { if (time < $ceoptimes{"$serv $chan $key"}) { } elsif ($chan =~ /$ceopkeys{$_}{chan}/i) { $ceoptimes{"$serv $chan $key"} = 120 + time; cmd_ceop_request("$user$_ $args", $serv, $win); } } } }; sub cmd_ceop_setkey { my ($args, $serv, $win, $trash) = @_; my $key = $args =~ s/^\s*(\S+)\s*// ? $1 : ""; $ceopkeys{$key}{user} = $key =~ s/(.*)\[\/,]// ? $1 : ""; $ceopkeys{$key}{level} = $args =~ s/^\s*(\d+)\s*// ? $1 : 0; $ceopkeys{$key}{age} = $args =~ s/^\s*(\d+)\s*// ? $1 : 0; $ceopkeys{$key}{chan} = $args =~ s/^\s*(\S+)\s*// ? $1 : ""; }; sub cmd_ceop_alias { my ($args, $serv, $win, $trash) = @_; $cmd = $args =~ s/^\s*(\S+)\s*// ? $1 : ""; $level = $args =~ s/^\s*(\d+)\s*// ? $1 : ""; $ceopcmds{$cmd} = $args; Irssi::settings_remove("ceop_command_level_$cmd"); Irssi::settings_add_int("ceop","ceop_command_level_$cmd",$level); } sub cmd_ceop_list { my ($args, $serv, $win, $trash) = @_; Irssi::print " Keys: (key, level, maxage, chan)"; Irssi::print "$ceopkeys{$_}{user},$_ $ceopkeys{$_}{level} $ceopkeys{$_}{age} $ceopkeys{$_}{chan}" for keys %ceopkeys; Irssi::print " Aliases: (name, level, command)"; Irssi::print "$_ ".Irssi::settings_get_int("ceop_command_level_$_")." $ceopcmds{$_}" for keys %ceopcmds; } Irssi::settings_add_int("ceop","ceop_default_command_level",0); Irssi::signal_add("ctcp msg op", "sig_ctcp_op"); Irssi::signal_add("ctcp msg ceop", "sig_ctcp_op"); Irssi::command_bind("ceop.request", "cmd_ceop_request"); Irssi::command_bind("ceop.autoop", "cmd_ceop_autoop"); Irssi::command_bind("ceop.setkey", "cmd_ceop_setkey"); Irssi::command_bind("ceop.alias", "cmd_ceop_alias"); Irssi::command_bind("ceop.list", "cmd_ceop_list"); 1;