# # Copyright (c) 2010-2013 by Nils Görs # # checks POP3 server for mails and display mail headers # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Config: # Add [mail] to your weechat.bar.status.items # # # 2021-05-05: Sébastien Helleu # 0.4 : add compatibility with XDG directories (WeeChat >= 3.2) # 2013-09-15: nils_2 (freenode.#weechat) # 0.3 : add: option prefix_item # # 2013-07-29: nils_2 (freenode.#weechat) # 0.2 : support of /secure for passwords # : added: %h variable for filename # # 0.1: initial version # # Thanks to Trashlord for the hint with hook_process() # # Development is currently hosted at # https://github.com/weechatter/weechat-scripts # # This script needs following perl modules: # Tie::IxHash # Mail::POP3Client # IO::Socket::SSL # MIME::Base64 # Crypt::Rijndael # You can install them using "cpan" # do not sort my hash use Tie::IxHash; my %pop3_accounts; my %mail_counts; # user@mail.de|pop3.server:port tie %pop3_accounts, Tie::IxHash; ### #use strict; use Mail::POP3Client; use MIME::Base64; use Crypt::Rijndael; use Encode; my $prgname = "pop3_mail"; my $SCRIPT_version = "0.4"; my $description = "check POP3 server for mails and display mail header"; my $item_name = "mail"; # -------------------------------[ config ]------------------------------------- my $default_pop3list = "%h/pop3list.txt"; my %default_options = ("refresh" => "10", # interval in minutes to check pop3 accounts "pop3_timeout" => "20", # timeout for pop3_server (in seconds) "show_header" => "From|Subject", "passphrase" => "enter passphrase", "delete_passphrase_on_exit" => "on", "prefix_item" => "✉:", ); # ------------------------------[ internal ]----------------------------------- my %Hooks = (); # space for my hooks my %mailcount = (); # how many mails for all accounts and in how many accounts? my $bar_item = ""; my $filename = (); my $weechat_version = ""; # -------------------------------[ hook_process() command ]------------------------------------- # arguments: user, password, server, port, timeout, (no)header if ($#ARGV == 5 ) { # six arguments given? my $user = $ARGV[0]; my $password = $ARGV[1]; my $pop3server = $ARGV[2]; my $port = $ARGV[3]; my $timeout = $ARGV[4]; my $header = $ARGV[5]; if ($port eq "995") { $use_ssl = 1; } else { $use_ssl = 0; } my $pop3 = new Mail::POP3Client( #DEBUG => 1, USER => $user, PASSWORD => $password, HOST => $pop3server, TIMEOUT => $timeout, # PORT => $port, USESSL => $use_ssl, # if true use port 995 otherwise port 110 ); my $num_mesg = $pop3->Count; # how many messages are there? if ($header ne "noheader"){ for( $i = 1; $i <= $pop3->Count(); $i++ ) { foreach( $pop3->Head( $i ) ) { my $decoded = encode("utf-8",decode("MIME-Header",$_)); /^($header):\s+/i && print "$decoded\n"; # decode MIME and encode to UTF8 } print "\n"; } }else{ print "$num_mesg\n"; } $pop3->Close(); exit; } # -------------------------------[ weechat program starts here ]------------------------------------- # check out config settings sub toggled_by_set{ my ( $pointer, $option, $value ) = @_; my $plugin_name = "plugins.var.perl.$prgname."; if ($option eq $plugin_name."refresh"){ $default_options{refresh} = $value; if (!weechat::config_is_set_plugin("refresh")){ $default_options{refresh} = 10; #default 10 minutes weechat::config_set_plugin("refresh", $default_options{refresh}); } if ($default_options{refresh} ne "0"){ if (defined $Hooks{timer}) { unhook_timer(); hook_timer(); return weechat::WEECHAT_RC_OK; } } if ($default_options{refresh} eq "0"){ if (defined $Hooks{timer}) { unhook_timer(); } }else{ if (not defined $Hooks{timer}){ weechat::config_set_plugin("refresh", "0") unless hook_timer(); # fall back to '0', if hook fails } } }elsif ($option eq $plugin_name."prefix_item"){ $default_options{prefix_item} = my_eval_expression($value); weechat::bar_item_update($item_name); }elsif ($option eq $plugin_name."pop3_timeout"){ $default_options{pop3_timeout} = $value; }elsif ($option eq $plugin_name."show_header"){ $default_options{show_header} = $value; }elsif ($option eq $plugin_name."passphrase"){ $default_options{passphrase} = my_eval_expression($value); return weechat::WEECHAT_RC_OK if (check_passphrase_length($default_options{passphrase}) eq 1); }elsif ($option eq $plugin_name."delete_passphrase_on_exit"){ $default_options{delete_passphrase_on_exit} = $value; } return weechat::WEECHAT_RC_OK; } sub hook_timer{ count_messages(); $Hooks{timer} = weechat::hook_timer($default_options{refresh} * 1000 * 60, 0, 0, "count_messages", ""); # period * millisec(1000) * second(60) * minutes(60) if ($Hooks{timer} eq '') { weechat::print("",weechat::prefix("error")."can't enable $prgname, hook failed."); return 0; } # $bar_item = weechat::bar_item_new($item_name, "show_mail",""); weechat::bar_item_update($item_name); return 1; } sub unhook_timer{ weechat::bar_item_remove($bar_item); $bar_item = ""; if (defined $Hooks{timer}){ weechat::unhook($Hooks{timer}); delete $Hooks{timer}; } } # user commands sub user_cmd{ my ($getargs) = ($_[2]); return weechat::WEECHAT_RC_OK if (check_passphrase_length($default_options{passphrase}) eq 1); if ($getargs eq ""){ weechat::command("","/help $prgname"); return weechat::WEECHAT_RC_OK; } if ($getargs eq "list"){ # list all accounts weechat::print("","POP3 accounts:"); my $i = 1; my $x = keys %pop3_accounts; if ($x eq 0){ weechat::print("","no accounts added yet"); }else{ while ( my($key,$password) = each %pop3_accounts) { my ($user,$pop3server) = split (/\|/,$key); # format : username|servername:port # $password = decode_Rijndael($password); $i = sprintf("%03d",$i); my ($servername, $port) = split(/:/,$pop3server); $port = $port . weechat::color("chat") . " (" . weechat::color("chat_delimiters") ."ssl" . weechat::color("chat") . ")" if ($port eq "995"); # get number of emails my $account = $user.$servername; my $num_mesg; if (defined $mail_counts{$account}){ $num_mesg = $mail_counts{$account}; }else{ $num_mesg = 0; } $num_mesg = sprintf("% 3d",$num_mesg); # print infos weechat::print( "", " [" . weechat::color("chat_delimiters") . $i . weechat::color("chat") . "]" . " mails: " . weechat::color("chat_delimiters") . $num_mesg . weechat::color("chat") . " user: " . weechat::color("chat_delimiters") . $user . weechat::color("chat") . " server: " . weechat::color("chat_delimiters") . $servername . weechat::color("chat") . " port: " . weechat::color("chat_delimiters") . $port . weechat::color("chat")); # . " password: " . $password); $i = $i + 1; } } return weechat::WEECHAT_RC_OK; } count_messages() if ($getargs eq "check"); my ( $cmd, $arg ) = ( $getargs =~ /(.*?)\s+(.*?)/ ); return weechat::WEECHAT_RC_OK if (not defined $cmd); if ($cmd eq "list" and defined $arg){ ( $cmd, $arg ) = ( $getargs =~ /(.*?)\s+(\d.*)/ ); if (not defined $cmd){ weechat::print("",weechat::prefix("error")."$prgname: wrong arguments"); return weechat::WEECHAT_RC_OK; } if ($cmd eq "list"){ # display messages on server for account my $i = 1; if ($arg !~ /^-?\d+(?:[\.,]\d+)?$/){ # only numbers for account? weechat::print("",weechat::prefix("error")."$prgname: invalid account number"); return weechat::WEECHAT_RC_OK; } foreach my $key ( keys %pop3_accounts ) { my $password = $pop3_accounts{$key}; if ($arg == $i){ get_message_header($key,$password); return weechat::WEECHAT_RC_OK; } $i = $i + 1; } return weechat::WEECHAT_RC_OK; } } if ($cmd eq "del" and defined($arg)){ # check command and if argument exists ( $cmd, $arg ) = ( $getargs =~ /(.*?)\s+(\d.*)/ ); my $i = 1; foreach my $key ( keys %pop3_accounts ) { if ($arg == $i){ delete $pop3_accounts{$key}; # delete user account save_file(); last; } $i = $i + 1; } unhook_timer(); hook_timer(); } if ($cmd eq "add" and defined($arg)){ my ( $cmd, $user, $host ,$password ) = ( $getargs =~ /(.*?)\s(.*?)\s(.*?)\s(.*)/ ); if (not defined $password or not defined $host or index($host,":") eq -1){ weechat::print("",weechat::prefix("error")."$prgname: wrong arguments given to add account: "); return weechat::WEECHAT_RC_OK; } $user = $user."|".$host; # username@adress|host $password = encode_Rijndael($password); chomp($password); $pop3_accounts{$user} = $password; # add new user account with password save_file(); unhook_timer(); hook_timer(); } return weechat::WEECHAT_RC_OK; } sub init { weechat::config_set_plugin("pop3_list", $default_pop3list ) if ( weechat::config_get_plugin("pop3_list") eq "" ); read_file(); # get absolute path of script my $infolist_pnt = weechat::infolist_get("perl_script","",$prgname); weechat::infolist_next($infolist_pnt); $filename = weechat::infolist_string($infolist_pnt,"filename"); weechat::infolist_free($infolist_pnt); #set default config foreach my $option (keys %default_options) { if ( !weechat::config_is_set_plugin($option) ) { weechat::config_set_plugin($option, $default_options{$option}); }else { $default_options{$option} = weechat::config_get_plugin($option); } } $default_options{passphrase} = my_eval_expression($default_options{passphrase}); $default_options{prefix_item} = my_eval_expression($default_options{prefix_item}); return if (check_passphrase_length($default_options{passphrase}) eq 1); } sub show_mail { if ( (keys %pop3_accounts) == 1 ){ return sprintf ("$default_options{prefix_item} %d", $mailcount{mails_over_all}); }else{ return sprintf ("$default_options{prefix_item} %d (%d)", $mailcount{mails_over_all}, $mailcount{accounts_with_mails}); } } # -------------------------------[ hook_process callback ]------------------------------------- sub hook_process_cb{ my ($data, $command, $return_code, $out, $err) = @_; return weechat::WEECHAT_RC_OK if ( $return_code > 0 ); # something went wrong! my (undef, undef, $nick, undef, $server, undef) = split /\s+/, $command, 6; my $account = $nick.$server; chomp($out); # kill LF $mailcount{num_mesg} = $out; # save number of messages $mailcount{num_mesg} = 0 if ($out == -1); # something wrong with account! $mailcount{accounts_with_mails} = $mailcount{accounts_with_mails} + 1 if ($out > 0); # how many accounts have mails? $mail_counts{$account} = $mailcount{num_mesg}; # save number of mails for the account $mailcount{mails_over_all} = $mailcount{mails_over_all} + $mailcount{num_mesg}; # all counted messages from all accounts if ( $bar_item ne "" and $mailcount{mails_over_all} == 0 or not defined $mailcount{mails_over_all} ){ weechat::bar_item_remove($bar_item); $bar_item = ""; }elsif ( ($mailcount{mails_over_all} > 0) and ($bar_item eq "") ){ $bar_item = weechat::bar_item_new($item_name, "show_mail",""); } weechat::bar_item_update($item_name); return weechat::WEECHAT_RC_OK; } # mail header sub hook_process_cb2{ my ($data, $command, $return_code, $out, $err) = @_; return weechat::WEECHAT_RC_OK if ( $return_code > 0 ); my (undef, undef, $nick, undef, $server, undef) = split /\s+/, $command, 6; my @array=split(/\n\n/,$out); my $i = 1; weechat::print( "", "mails for \"$nick\" on server \"$server\":"); foreach (@array) { weechat::print( "", weechat::color("chat_delimiters") . "[" . weechat::color("chat_buffer") . " mail " . $i . weechat::color("chat_delimiters") . " ]" . weechat::color("chat") . "\n$_" ); $i++; } return weechat::WEECHAT_RC_OK; } # -------------------------------[ POP3 routines ]------------------------------------- sub count_messages{ $mailcount{num_mesg} = 0; $mailcount{mails_over_all} = 0; $mailcount{accounts_with_mails} = 0; foreach my $key ( keys %pop3_accounts ) { my ($user,$pop3server,$port) = split (/:|\|/,$key); # format : username|servername:port my $password = decode_Rijndael($pop3_accounts{$key}); weechat::hook_process("perl $filename $user $password $pop3server $port $default_options{pop3_timeout} noheader", 1100 * $default_options{pop3_timeout},"hook_process_cb",""); } return weechat::WEECHAT_RC_OK; } sub get_message_header { my ($key,$password) = @_; my $i; my $use_ssl = 0; if ($default_options{show_header} eq "" or not defined $default_options{show_header}){ weechat::print("",weechat::prefix("error")."$prgname: option \"plugins.var.perl.$prgname.show_header\" not set."); return; } my ($user,$pop3server,$port) = split (/:|\|/,$key); # format : username|servername:port my $account = $user.$pop3server; if (not defined $mail_counts{$account} or $mail_counts{$account} eq 0){ weechat::print("",weechat::prefix("error")."$prgname: no mails on server \"$pop3server\" for \"$user\""); return; } $password = decode_Rijndael($password); weechat::hook_process("perl $filename $user $password $pop3server $port $default_options{pop3_timeout} \"$default_options{show_header}\"", 1100 * $default_options{pop3_timeout},"hook_process_cb2",""); } # -------------------------------[ AES encode / decode: key must be 128, 192 or 256 bits long ]------------------------------------- sub encode_Rijndael{ my ( $plaintext ) = @_; # Your string to be encrypted my $base64 = ""; my $rcipher = Crypt::Rijndael->new ($default_options{passphrase}, Crypt::Rijndael::MODE_CBC()); $rcipher->set_iv($default_options{passphrase}); # You may wish this IV to be something different from the Secret Key if(length($plaintext) % 16 != 0 ) { $plaintext .= ' ' x (16 - (length($plaintext) % 16)); } my $rencrypted = $rcipher->encrypt($plaintext); # encrypt $base64 = encode_base64($rencrypted); # base64 it return $base64; } sub decode_Rijndael{ my ( $base64 ) = @_; # enctypted base64 string my $rcipher = Crypt::Rijndael->new ($default_options{passphrase}, Crypt::Rijndael::MODE_CBC()); $rcipher->set_iv($default_options{passphrase}); # You may wish this IV to be something different from the Secret Key $base64 = decode_base64($base64); # if(length($plaintext) % 16 != 0 ) { # $plaintext .= ' ' x (16 - (length($plaintext) % 16)); } my $rencrypted = $rcipher->decrypt($base64); $rencrypted =~ s/\s+\z//; # remove space from end of string return $rencrypted; } sub check_passphrase_length { my ( $passphrase ) = @_; my $len = length($passphrase); if (! ($len == 16) or ($len == 24) or ($len == 32) ) { weechat::print("",weechat::prefix("error")."$prgname: wrong key length: passphrase must be 128, 192 or 256 bits long"); return 1; #false } return 0; #true } sub my_eval_expression { my $value = $_[0]; if ( ($weechat_version ne "") && ($weechat_version >= 0x00040200) ) { my $eval_expression = weechat::string_eval_expression($value,{},{},{}); return $eval_expression if ($eval_expression ne ""); } return $value; } # -------------------------------[ load, save, shutdown, debug routine ]------------------------------------- sub save_file { my $x = keys %pop3_accounts; if ($x ne 0) # messages in pop3_accounts? { my $pop3list = weechat_dir(); open (WL, ">", $pop3list) || DEBUG("write pop3_list: $!"); while ( my($user,$passwort) = each %pop3_accounts) { print WL "$user $passwort\n"; } close WL; } else { my $pop3list = weechat_dir(); unlink($pop3list); } } sub read_file { my $pop3list = weechat_dir(); return unless -e $pop3list; open (WL, "<", $pop3list) || DEBUG("$pop3list: $!"); while () { chomp; # kill LF my ( $user, $password ) = split / /; # if (not defined $user) { close WL; weechat::print("",weechat::prefix("error")."$prgname: $pop3list is not valid..."); return; } $pop3_accounts{$user} = $password if length $_; } close WL; } sub weechat_dir { my $options = { "directory" => "config" }; return weechat::string_eval_path_home(weechat::config_get_plugin("pop3_list"), {}, {}, $options); } sub shutdown{ # remove my hooks if (defined $Hooks{timer}) { weechat::unhook($Hooks{timer}); } if (defined $Hooks{command}) { weechat::unhook($Hooks{command}); } if (defined $Hooks{config}) { weechat::unhook($Hooks{config}); } if ($default_options{delete_passphrase_on_exit} eq "on"){ weechat::config_unset_plugin("passphrase",""); } return weechat::WEECHAT_RC_OK; } sub DEBUG {weechat::print('', "***\t" . $_[0]);} # first function called by a WeeChat-script. weechat::register($prgname, "Nils Görs ", $SCRIPT_version, "GPL3", $description, "shutdown", ""); $weechat_version = weechat::info_get("version_number", ""); init(); $Hooks{config} = weechat::hook_config( "plugins.var.perl.$prgname.*", "toggled_by_set", "" ); $Hooks{command} = weechat::hook_command($prgname, $description, "[list ] | [add user(\@host) server:port password] | [del number] | check", "list : display account(s)\n". "list : display mail header(s) for specified account\n". "add : add new account (template: )\n". "del : delete an account\n". "check : check POP3 account(s) manually\n". "\n". "This script is using Rijndael(AES) encryption to protect your pop3 password(s) in config file.\n". "Keep in mind that the passphrase is not encrypted. Use /rmodifier function to hide passphrase.\n". "Using Weechat 0.4.2 or higher its recommended to use /secure to protect passphrase.\n". "\n". "Options:\n". "plugins.var.perl.$prgname.passphrase : to encrypt pop3 passwords in config file (default: empty)\n". " Since WeeChat 0.4.2 its possible to encrypt passphrase (see /help secure) eg: \${sec.data.pop3_passphrase}\n". "plugins.var.perl.$prgname.pop3_list : file to store account, server and password (default: %h/pop3list.txt)\n". "plugins.var.perl.$prgname.pop3timeout : set a timeout (in seconds) for socket operations (default: 20)\n". "plugins.var.perl.$prgname.refresh : checks pop3 account (in minutes) (default: 10)\n". "plugins.var.perl.$prgname.show_header : displays mail headers (default: From|Subject)\n". "plugins.var.perl.$prgname.prefix_item : displays a prefix (default: ✉:). Since WeeChat 0.4.2 you can use \${color:xxx}\n". "plugins.var.perl.$prgname.delete_passphrase_on_exit : delete passphrase on exit (default: on)\n". "\n". "You have to edit option \"delete_passphrase_on_exit\" manually each time when weechat or script (re)starts. Switching option to \"off\" will keep passphrase in weechat-config.\n". "\n". "Add item [mail] to your \"weechat.bar.status.items\"\n". "\n". "Examples:\n". "Add account with hostname and ssl/tls protocol to monitore:\n". "/$prgname add myuser\@host.de pop3.server.de:995 mypassword\n". "Add account without hostname and without ssl/tls protocol to monitore:\n". "/$prgname add myuser pop3.server.de:110 mypassword\n". "Delete account with number 001 from list:\n". "/$prgname del 1\n", "add|del|list|check", "user_cmd", ""); hook_timer() if ($default_options{refresh} ne "0"); weechat::bar_item_update($item_name);