html/securemsg.pl


   1 # Thanks to Geert and Karl "Sique" Siegemund on their work on the whitelist.pl script that is the basis for my work.
   2 # 
   3 # Supports multiple servers
   4 #
   5 # /set securemsg_nicks phyber etc
   6 # nicks that are allowed to msg us (whitelist checks for a valid nick before a valid host)
   7 #
   8 # /set securemsg_hosts *!*@*isp.com *!ident@somewhere.org
   9 # Hosts that are allowed to message us, space delimited
  10 #
  11 # /sm add nick <list of nicks>
  12 # puts new nicks into the whitelist_nicks list
  13 #
  14 # /sm add host <list of hosts>
  15 # puts new hosts into the whitelist_hosts list
  16 #
  17 # /sm del nick <list of nicks>
  18 # removes the nicks from whitelist_nicks
  19 #
  20 # /sm del host <list of hosts>
  21 # removes the hosts from whitelist_hosts
  22 #
  23 # /sm nicks
  24 # shows the current whitelist_nicks
  25 #
  26 # /sm hosts
  27 # shows the current whitelist_hosts
  28 #
  29 # /sm accept <nick> [message]
  30 # accept chat from nick
  31 #
  32 # /sm reject <nick> [message]
  33 # reject chat from nick
  34 #
  35 # /sm clear <nick>
  36 # clears messages from nick without ignoring
  37 #
  38 ##
  39 
  40 use strict;
  41 use Irssi;
  42 use Irssi::Irc;
  43 use Irssi::UI;
  44 use Irssi::TextUI;
  45 
  46 
  47 use vars qw($VERSION %IRSSI);
  48 $VERSION = "2.1";
  49 my $APPVERSION = "Securemsg v2.1";
  50 %IRSSI = (
  51 	  authors	=> "Jari Matilainen, a lot of code borrowed from whitelist.pl by David O\'Rourke and Karl Siegemund",
  52 	  contact	=> "vague`!#irssi\@freenode on irc ",
  53 	  name		=> "securemsg",
  54 	  description	=> "An irssi adaptation of securequery.mrc found in the Acidmax mIRC script. :), now with multiserver support",
  55 	  licence	=> "GPLv2",
  56 	  changed	=> "10.09.2007 11:30pm GST"
  57 );
  58 
  59 my $whitenick;
  60 my $whitehost;
  61 my $tstamp;
  62 my %messages = ();
  63 
  64 # A mapping to convert simple regexp (* and ?) into Perl regexp
  65 my %htr = ( );
  66 foreach my $i (0..255) {
  67     my $ch = chr($i);
  68     $htr{$ch} = "\Q$ch\E";
  69 }
  70 $htr{'?'} = '.';
  71 $htr{'*'} = '.*';
  72 
  73 # A list of settings we can use and change
  74 my %types = ( 'nicks'    => 'securemsg_nicks',
  75 	      'hosts'    => 'securemsg_hosts' );
  76 
  77 sub lc_host($) {
  78     my ($host) = @_;
  79     $host =~ s/(.+)\@(.+)/sprintf("%s@%s", $1, lc($2));/eg;
  80     return $host;
  81 }
  82 
  83 sub timestamp {
  84     my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  85     my @timestamp = ($year+1900,$mon+1,$mday,$hour,$min,$sec);
  86     if($timestamp[1]<10) {
  87         $timestamp[1] = "0".$timestamp[1];
  88     }
  89     if($timestamp[2]<10) {
  90         $timestamp[2] = "0".$timestamp[2];
  91     }
  92     if($timestamp[3]<10) {
  93         $timestamp[3] = "0".$timestamp[3];
  94     }
  95     if($timestamp[4]<10) {
  96         $timestamp[4] = "0".$timestamp[4];
  97     }
  98     $tstamp = "$timestamp[0]/$timestamp[1]/$timestamp[2] $timestamp[3]:$timestamp[4] ";
  99 }
 100 
 101 # This one gets called from IRSSI if we get a private message (PRIVMSG)
 102 sub securemsg_check {
 103     my ($server, $msg, $nick, $address) = @_;
 104     my $nicks         = Irssi::settings_get_str('securemsg_nicks');
 105     my $hosts         = Irssi::settings_get_str('securemsg_hosts');
 106     my $mmsg          = Irssi::settings_get_str('securemsg_customhold');
 107     my $hostmask      = "$nick!$address";
 108     $nick = lc($nick);
 109     return if $server->ignore_check($nick,"","","",MSGLEVEL_MSGS);
 110 
 111     # Are we already talking?
 112     return if $server->query_find($nick);
 113     return if $server->{nick} eq $nick;
 114 
 115     # Nicks are the easiest to handle with the least computational effort.
 116     # So do them before hosts and networks.
 117     foreach my $whitenick (split(/\s+/, "$nicks")) {
 118         $nick = lc($nick);
 119         $whitenick = lc($whitenick);
 120 	# Simple check first: Is the nick itself whitelisted?
 121 	return if ($nick eq $whitenick);
 122     }
 123     
 124     # Hostmasks are somewhat more sophisticated, because they allow wildcards
 125     foreach my $whitehost (split(/\s+/, "$hosts")) {
 126 	# Allow if the hostmask matches
 127 	return if $server->mask_match_address($whitehost,"*",$hostmask);
 128     }
 129 
 130     # stop if the message isn't from a whitelisted address
 131     # print a notice if that setting is enabled
 132     # this could flood your status window if someone is flooding you with messages
 133 
 134     if ((!defined $mmsg) || ($mmsg eq " ") || ($mmsg eq "")) {
 135         $mmsg = "Please standby for acknowledgement. I am using «$APPVERSION» for irssi. You will be notified if accepted. Until then your messages will be ignored.";
 136     }
 137     else {
 138 	$mmsg = $mmsg." «$APPVERSION»";
 139     }
 140 
 141     $server->command("^NOTICE $nick $mmsg") if(!exists $messages{$nick}{$server->{tag}});
 142 
 143     #Save message from $nick
 144     timestamp();
 145     my $channel;
 146     my @channels = $server->channels();
 147     foreach my $chan (@channels) {
 148         if($chan->nick_find_mask($nick)) {
 149             $channel = $chan;
 150             last;
 151         }
 152     }
 153     
 154     my $tmpmsg = $tstamp."<".$nick."!".(($channel)?$channel->{name}:"none")."@".lc($server->{tag})."> ".$msg;
 155     push @{$messages{$nick}{lc($server->{tag})}{messages}},$tmpmsg;
 156 
 157     refresh_securemsg();
 158     Irssi::command_bind("sm accept $nick",\&cmd_accept);
 159     Irssi::command_bind("sm reject $nick",\&cmd_reject);
 160     Irssi::command_bind("sm accept $nick net",\&cmd_accept);
 161     Irssi::command_bind("sm reject $nick net",\&cmd_reject);
 162     Irssi::command_bind("sm accept $nick net ".lc($server->{tag}),\&cmd_accept);
 163     Irssi::command_bind("sm reject $nick net ".lc($server->{tag}),\&cmd_reject);
 164     Irssi::command_bind("sm show $nick",\&cmd_show);
 165     Irssi::command_bind("sm show $nick net ",\&cmd_show);
 166     Irssi::command_bind("sm show $nick net ".lc($server->{tag}),\&cmd_show);
 167     Irssi::signal_stop();
 168     return;
 169 }
 170 
 171 sub usage {
 172     print("Usage: sm add|del <nick>|<host> list of nicks/hosts | sm nicks|hosts | sm accept|reject <nick> [net <chatnet>] [message] | sm show <nick> [net <chatnet>] | sm help");
 173 }
 174 
 175 sub cmd_accept {
 176     my ($args, $active_server, $witem) = @_;
 177     my ($nick, $rest) = split /\s+/, $args, 2;
 178     my ($arg, $net);
 179     my $server;
 180     my $mmsg          = Irssi::settings_get_str('securemsg_customaccept');
 181     my $msg;
 182 
 183     if($rest =~ /^net/) {
 184 	($arg, $net, $rest) = split /\s+/, $rest, 3;
 185     }
 186 
 187     $nick = lc($nick);
 188     $net = lc($net);
 189 
 190     if((!defined $nick) || ($nick eq "")) {
 191 	usage;
 192 	return;
 193     }
 194 
 195     return if(!exists $messages{$nick});
 196 
 197     if((!defined $rest) || ($rest eq " ") || ($rest eq "")) {
 198 	if((!defined $mmsg) || ($mmsg eq " ") || ($mmsg eq "")) {
 199             $rest = "Hold on, I'm switching windows...";
 200 	}
 201 	else {
 202 	    $rest = $mmsg;
 203 	}
 204     }
 205 
 206     if(defined $net && !(($net eq " ") || ($net eq ""))) {
 207 	foreach (keys %{$messages{$nick}}) {
 208 	    if(lc($_) eq $net) {
 209 		$server = Irssi::server_find_tag($_);
 210 		last;
 211 	    }
 212 	}
 213     }
 214     elsif(keys(%{$messages{$nick}}) == 1) {
 215 	foreach (keys %{$messages{$nick}}) {
 216 	    $server = Irssi::server_find_tag($_);
 217 	}
 218     }
 219     else {
 220 	print("You have to specify a chatnet, for example /sm accept john net EFNet");
 221 	return;
 222     }
 223 
 224     $server->command("MSG $nick $rest");
 225 
 226     my $query = $server->query_find($nick);
 227     $query->set_active();
 228     foreach $msg (@{$messages{$nick}{lc($server->{tag})}{messages}}) {
 229         $query->print("%B-%W!%B-%n $msg", MSGLEVEL_CLIENTCRAP);
 230     }
 231 
 232     if(keys(%{$messages{$nick}}) > 1) {
 233         delete $messages{$nick}{lc($server->{tag})};
 234     }
 235     else {
 236         delete $messages{$nick};
 237     }
 238 
 239     Irssi::command_unbind("sm accept $nick",\&cmd_accept);
 240     Irssi::command_unbind("sm reject $nick",\&cmd_reject);
 241     Irssi::command_unbind("sm accept $nick net",\&cmd_accept);
 242     Irssi::command_unbind("sm reject $nick net",\&cmd_reject);
 243     Irssi::command_unbind("sm accept $nick net ".lc($server->{tag}),\&cmd_accept);
 244     Irssi::command_unbind("sm reject $nick net ".lc($server->{tag}),\&cmd_reject);
 245     Irssi::command_unbind("sm show $nick",\&cmd_show);
 246     Irssi::command_unbind("sm show $nick net ",\&cmd_show);
 247     Irssi::command_unbind("sm show $nick net ".lc($server->{tag}),\&cmd_show);
 248     refresh_securemsg();
 249 }
 250 
 251 sub cmd_reject {
 252     my ($args, $active_server, $winit) = @_;
 253     my ($nick, $rest) = split /\s+/, $args, 2;
 254     my $time          = Irssi::settings_get_str('securemsg_ignoretime');
 255     my ($arg, $net);
 256     my $server;
 257 
 258     if($rest =~ /^net/) {
 259         ($arg, $net, $rest) = split /\s+/, $rest, 3;
 260     }
 261 
 262     if((!defined $time) || ($time eq "") || ($time eq " ")) {
 263 	$time = "600";
 264     }
 265 
 266     $nick = lc($nick);
 267     $net = lc($net);
 268 
 269     if((!defined $nick) || ($nick eq "")) {
 270 	usage;
 271 	return;
 272     }
 273 
 274     if(defined $net && !(($net eq " ") || ($net eq ""))) {
 275         foreach (keys %{$messages{$nick}}) {
 276             if(lc($_) eq $net) {
 277                 $server = Irssi::server_find_tag($_);
 278                 last;
 279             }
 280         }
 281     }
 282     elsif(keys(%{$messages{$nick}}) == 1) {
 283         foreach (keys %{$messages{$nick}}) {
 284             $server = Irssi::server_find_tag($_);
 285         }
 286     }
 287     else {
 288         print("You have to specify a chatnet, for example /sm reject john net EFNet");
 289         return;
 290     }
 291 
 292     if((defined $rest) && !(($rest eq " ") || ($rest eq ""))) {
 293         $server->command("^NOTICE $nick $rest");
 294     }
 295 
 296     if(keys(%{$messages{$nick}}) > 1) {
 297 	delete $messages{$nick}{lc($server->{tag})};
 298     }
 299     else {
 300 	delete $messages{$nick};
 301     }
 302 
 303     Irssi::command_unbind("sm accept $nick",\&cmd_accept);
 304     Irssi::command_unbind("sm reject $nick",\&cmd_reject);
 305     Irssi::command_unbind("sm accept $nick net",\&cmd_accept);
 306     Irssi::command_unbind("sm reject $nick net",\&cmd_reject);
 307     Irssi::command_unbind("sm accept $nick net ".lc($server->{tag}),\&cmd_accept);
 308     Irssi::command_unbind("sm reject $nick net ".lc($server->{tag}),\&cmd_reject);
 309     Irssi::command_unbind("sm show $nick",\&cmd_show);
 310     Irssi::command_unbind("sm show $nick net ",\&cmd_show);
 311     Irssi::command_unbind("sm show $nick net ".lc($server->{tag}),\&cmd_show);
 312     $server->command("^IGNORE -time $time $nick MSGS DCCMSGS NOTICES");
 313     refresh_securemsg();
 314 }
 315 
 316 sub cmd_add {
 317     my ($args, $server, $witem) = @_;
 318     my $str = '';
 319     my @list = ( );
 320     my ($type, $rest) = split /\s+/, $args, 2;
 321 
 322     # What type of settings we want to change?
 323     if (($type eq "nick") || ($type eq "host")) {
 324         $type = $type."s";
 325     } 
 326     my $settings = $types{$type};
 327 
 328     # If we didn't get a syntactically correct command, put out an error
 329     if(!defined $settings && defined $type) {
 330         usage;
 331         return;
 332     }
 333 
 334     # Get the current value of the setting we want to change
 335     my $str = Irssi::settings_get_str($settings) if defined $settings;
 336     # What are we doing?
 337     # Add the list to the end
 338     $str .= " $rest";
 339     # Convert into an array
 340     @list = split /\s+/, $str;
 341     # Make the array unique (see Perl FAQ)
 342     undef my %saw;
 343     @list = grep(!$saw{$_}++, @list);
 344     # Put the array together
 345     $str = join ' ', @list;
 346 
 347     print "SecureMsg ${type}: $str";
 348     Irssi::settings_set_str($settings, $str);
 349 }
 350 
 351 sub cmd_del {
 352     my ($args, $server, $witem) = @_;
 353     my @list = ( );
 354     my ($type, $rest) = split /\s+/, $args, 2;
 355 
 356     # What type of settings we want to change?
 357     if (($type eq "nick") || ($type eq "host")) {
 358         $type = $type."s";
 359     }
 360     my $settings = $types{$type};
 361 
 362     # If we didn't get a syntactically correct command, put out an error
 363     if(!defined $settings && defined $type) {
 364         usage;
 365         return;
 366     }
 367 
 368     my $str = Irssi::settings_get_str($settings);
 369 
 370     # Convert the list into an array
 371     @list = split /\s+/, $str;
 372     # Escape all letters to protect the Perl Regexp special characters
 373     $rest =~ s/(.)/$htr{$1}/g;
 374     # Convert the removal list into a Perl regexp
 375     $rest =~ s/\s+/$|^/g;
 376     # Use grep() to filter out all occurences of the removal list
 377     $str = join(' ', grep {!/^$rest$/} @list);
 378 
 379     print "SecureMsg ${type}: $str";
 380     Irssi::settings_set_str($settings, $str);
 381 }
 382 
 383 sub cmd_nicks {
 384     print "SecureMsg nicks: ".Irssi::settings_get_str($types{nicks});
 385 }
 386 
 387 sub cmd_hosts {
 388     print "SecureMsg hosts: ".Irssi::settings_get_str($types{hosts});
 389 }
 390 
 391 sub cmd_help {
 392 #    usage;
 393     print ( <<EOF
 394 Commands:
 395 SM HELP                                    - SHOWS THIS HELP
 396 SM ADD NICK|HOST <nicks>|<hosts>           - ADDS/DELETES A SPACE SEPARATED LIST OF NICKS OR HOSTS
 397 SM NICKS|HOSTS                             - DISPLAYS THE CURRENT WHITELISTED NICKS OR HOSTS
 398 SM ACCEPT <nick> [net <chatnet>] [message] - ALLOWS MSG'S FROM nick
 399 SM REJECT <nick> [net <chatnet>] [message] - DOESN'T ALLOW MESSAGES FROM nick
 400 SM SHOW <nick> [net <chatnet>]             - SHOWS CURRENT MESSAGES FROM nick WITHOUT ACCEPTING OR REJECTING nick
 401 EOF
 402     );
 403 }
 404 
 405 sub cmd_show {
 406     my ($args, $server, $witem) = @_;
 407     my ($nick, $rest) = split /\s+/, $args, 2;
 408     my ($arg, $net);
 409     my $server;
 410 
 411     if($rest =~ /^net/) {
 412         ($arg, $net) = split /\s+/, $rest, 2;
 413     }
 414 
 415     $net = lc($net);
 416 
 417     if((!defined $nick) || (!exists $messages{$nick})) {
 418 	usage;
 419 	return;
 420     }
 421 
 422     if(defined $net && !(($net eq " ") || ($net eq ""))) {
 423         foreach (keys %{$messages{$nick}}) {
 424             if($_ eq $net) {
 425                 $server = Irssi::server_find_tag($_);
 426                 last;
 427             }
 428         }
 429     }
 430     elsif(keys(%{$messages{$nick}}) == 1) {  
 431         foreach (keys %{$messages{$nick}}) {
 432             $server = Irssi::server_find_tag($_);
 433         }
 434     }
 435     else {
 436         print("You have to specify a chatnet, for example /sm reject john net EFNet");
 437         return;
 438     }
 439 
 440     foreach my $msg (@{$messages{$nick}{lc($server->{tag})}{messages}}) {
 441         print CLIENTCRAP "%B-%W!%B-%n $msg";
 442     }
 443 }
 444 
 445 sub securemsg {
 446     my ($item,$get_size_only) = @_;
 447     my $result = 0;
 448     my $nicks = "";
 449     foreach my $nick (keys %messages) {
 450 	foreach my $tag (keys %{$messages{$nick}}) {
 451 	    if ($nicks eq "") {
 452 		if(keys %{$messages{$nick}} > 1) {
 453 		    $nicks = $nick."@".$tag;
 454 		}
 455 		else {
 456 		    $nicks = $nick;
 457 		}
 458 	    }
 459 	    else {
 460 		if(keys %{$messages{$nick}} > 1) {
 461 		    $nicks = $nicks.", ".$nick."@".$tag;
 462 		}
 463 		else {
 464 		    $nicks = $nicks.", ".$nick;
 465 		}
 466 	    }
 467 	    $result++;
 468 	}
 469     }
 470     if ($result ne 0) {
 471         $result = $result." - \%_$nicks\%_";
 472     }
 473     $item->default_handler($get_size_only, undef, $result, 0);
 474 }
 475 
 476 sub refresh_securemsg {
 477   Irssi::statusbar_items_redraw('securemsg');
 478 }
 479 
 480 foreach (keys(%types)) {
 481     Irssi::settings_add_str('SecureMsg', $types{$_}, '');
 482 }
 483 
 484 Irssi::settings_add_str('Securemsg', 'securemsg_customhold', '');
 485 Irssi::settings_add_str('Securemsg', 'securemsg_customaccept', '');
 486 Irssi::settings_add_str('Securemsg', 'securemsg_ignoretime', '');
 487 
 488 Irssi::signal_add_first('message private', \&securemsg_check);
 489 Irssi::statusbar_item_register('securemsg', '{sb msgs: $0-}', 'securemsg');
 490 
 491 Irssi::command_bind 'sm' => sub {
 492     my ( $data, $server, $item ) = @_;
 493     $data =~ s/\s+$//g;
 494     Irssi::command_runsub ('sm', $data, $server, $item ) ;
 495 };
 496 Irssi::command_bind('sm add',\&cmd_add);
 497 Irssi::command_bind('sm del',\&cmd_del);
 498 Irssi::command_bind('sm nicks',\&cmd_nicks);
 499 Irssi::command_bind('sm hosts',\&cmd_hosts);
 500 Irssi::command_bind('sm show',\&cmd_show);
 501 Irssi::command_bind('sm accept',\&cmd_accept);
 502 Irssi::command_bind('sm reject',\&cmd_reject);
 503 Irssi::command_bind('sm help',\&cmd_help);
 504 Irssi::command_bind('sm add host',\&cmd_add);
 505 Irssi::command_bind('sm add nick',\&cmd_add);
 506 Irssi::command_bind('sm del host',\&cmd_del);
 507 Irssi::command_bind('sm del nick',\&cmd_del);
 508 
 509 refresh_securemsg();