diff options
Diffstat (limited to '')
-rw-r--r-- | scripts/mailcheck_imap.pl | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/scripts/mailcheck_imap.pl b/scripts/mailcheck_imap.pl new file mode 100644 index 0000000..b1ee937 --- /dev/null +++ b/scripts/mailcheck_imap.pl @@ -0,0 +1,566 @@ +# mailcheck_imap.pl + +# Contains code from centericq.pl (public domain) and imapbiff (GPL) and +# hence this is also GPL'd. + +use strict; +use vars qw($VERSION %IRSSI); +$VERSION = "0.5"; +%IRSSI = ( + authors => "David \"Legooolas\" Gardner", + contact => "irssi\@icmfp.com", + name => "mailcheck_imap", + description => "Staturbar item which indicates how many new emails you have in the specified IMAP[S] mailbox", + sbitems => "mailcheck_imap", + license => "GNU GPLv2", + url => "http://icmfp.com/irssi", +); + + +# TODO: +# +# - command to show status, so we can see if we are currently connected +# - add to statusbar item to say connected/not +# +# ? get user to type in password instead of storing it in a setting... +# - eg. /mailcheck_imap_pass <password> +# +# - settings +# - execute arbitrary command (with /exec?) on new mail? +# - for 'spoing' or something ;) +# - auto-reconnect on/off +# +# +# LATER: +# - show subject/sender/whatever of new mail (customizable) +# - multiple accounts? +# - multiple mailboxes? + + +# Known bugs: segfaults on exit of irssi when script loaded :/ + + +use Irssi; +use Irssi::TextUI; +use IO::Socket; + +# TODO : avoid requiring SSL when it's not in use? +#if (Irssi::settings_get_bool('mailcheck_imap_use_ssl')) { +# Irssi::print("Using SSL.") if $debug_msgs; +# $port = 993; + require IO::Socket::SSL; +# - you need the package libio-socket-ssl-perl on Debian +#} + +# +# TODO : Set up signal handling for clean shutdown... +# +#$SIG{'ALRM'} = sub { die "socket timeout" }; +#$SIG{'QUIT'} = 'cleanup'; +#$SIG{'HUP'} = 'cleanup'; +#$SIG{'INT'} = 'cleanup'; +#$SIG{'KILL'} = 'cleanup'; +#$SIG{'TERM'} = 'cleanup'; + + + +sub draw_box ($$$$) { + my ($title, $text, $footer, $colour) = @_; + my $box = ''; + $box .= '%R,--[%n%9%U'.$title.'%U%9%R]%n'."\n"; + foreach (split(/\n/, $text)) { + $box .= '%R|%n '.$_."\n"; + } + $box .= '%R`--<%n'.$footer.'%R>->%n'; + $box =~ s/%.//g unless $colour; + return $box; +} + + +sub show_help() { + my $help = $IRSSI{name}." ".$VERSION." +/mailcheck_imap_help + Display this help. +/mailcheck_imap + Check for new mail immediately, opening the connection if required. +/mailcheck_imap_stop + Close connection to server and stop checking for new mail. +/set mailcheck_imap + Show all mailcheck_imap settings. + Note: You need to set at least host, user and password. +/statusbar <name> add mailcheck_imap + Add statusbar item for mailcheck. + + +Formats in theme for statusbar item: +(number of new mails in $0, total number of message in $1) + sb_mailcheck_imap = \"{sb Mail: $0 new, $1 total}\"; + sb_mailcheck_imap_zero = \"{sb Mail: None new, $1 total}\"; + +Format in theme for 'new mail arrived' message in current window: +(number of new mails in $0, total number of message in $1) + mailcheck_imap_echo = \"You have $0 new message(s)!\"; + +Note: You have to set at least the mailcheck_imap_host, user, + and password settings. + +IMPORTANT NOTE: As this stores the password in your irssi config +file, you should really set the mode of the file to 0600 so that +it's only readable by your user. +"; + my $text = ""; + foreach (split(/\n/, $help)) { + $_ =~ s/^\/(.*)$/%9\/$1%9/; + $text .= $_."\n"; + } + print CLIENTCRAP draw_box($IRSSI{name}, $text, "Help", 1); +} + + +sub cmd_mailcheck_imap_help { + show_help(); +} + + +# +# Global variables. +# +my $handle; +my ($logged_in, $sleep); +my ($last_refresh_time, $refresh_tag); +my ($new_messages, $old_new_messages); +my ($total_messages, $old_total_messages); + +$handle = 0; +$logged_in = 0; +$old_new_messages = -1; +$old_total_messages = -1; + + +# +# Subroutine to update status, called every N seconds. +# +sub refresh_mailcheck_imap { + + # For now, just print a message and return :) + Irssi::print("update hit.") if Irssi::settings_get_bool('mailcheck_imap_debug'); + + # ensure we have details for the login.. + if(!check_details()) { + return 0; + } + + if(!$handle) { + if(!setup_socket()) { + error("Couldn't setup socket to imap server!",0); + return 0; + } + } + Irssi::print("Socket is setup.") if Irssi::settings_get_bool('mailcheck_imap_debug'); + + if(!$logged_in) { + if(!login()) { + return 0; + } + } + $new_messages = check_imap("UNSEEN"); + $total_messages = check_imap("MESSAGES"); + + $new_messages = 0 if (! $new_messages); + $total_messages = 0 if (! $total_messages); + + if ($new_messages eq "-1" || $total_messages eq "-1") { + Irssi::print("check_imap returned an error, no updates.") if Irssi::settings_get_bool('mailcheck_imap_debug'); + } + + # update statusbar if changed rather than updating every the time... + if(($new_messages != $old_new_messages) || + ($total_messages != $old_total_messages)) { + update_statusbar_item(); + } + + + # TODO : This doesn't work if you get a sequence such as: + # check -> arrive, delete, arrive -> check + # as it is just done on the number of unseen messages and won't know.. + if(($new_messages > $old_new_messages) && + (Irssi::settings_get_bool('mailcheck_imap_echo_new_in_window'))) { + # If set, echo to the current window... + my $theme = Irssi::current_theme(); + my $format = $theme->format_expand("{mailcheck_imap_echo}"); + + if ($format) { + # use theme-specific look + $format = $theme->format_expand("{mailcheck_imap_echo $new_messages $total_messages}", Irssi::EXPAND_FLAG_IGNORE_REPLACES); + } else { + # use the default look + $format = "mailcheck_imap: You have ".$new_messages." new message(s)."; + } + + print CLIENTCRAP $format; + } + $old_new_messages = $new_messages; + $old_total_messages = $total_messages; + + # Adding new timeout to make sure that this function will be called again + if ($refresh_tag) { + Irssi::timeout_remove($refresh_tag); + } + my $time = Irssi::settings_get_int('mailcheck_imap_interval'); + $refresh_tag = Irssi::timeout_add($time*1000, 'refresh_mailcheck_imap', undef); + + return 1; +} + + +# +# Subroutine to setup socket handle. +# +sub setup_socket { + # Set an alarm in case we can not connect or get hung. Older versions + # the IO::Socket perl module caused errors with the alarm we set before + # setting up the socket. If this program dies in debug mode saying: + # "Alarm clock", then you can probably fix it by upgrading your perl + # IO module. + my ($host,$port); + + $host = Irssi::settings_get_str('mailcheck_imap_host'); + $port = Irssi::settings_get_int('mailcheck_imap_port'); + + # change port number if SSL enabled and original imap port unchanged + if($port == 143 && Irssi::settings_get_bool('mailcheck_imap_use_ssl')) { + $port = 993; + } + + eval { + alarm 30; + Irssi::print("mailcheck_imap connecting to mail server..."); + + if (Irssi::settings_get_bool('mailcheck_imap_use_ssl')) { + Irssi::print("Using ssl...") if Irssi::settings_get_bool('mailcheck_imap_debug'); + $handle = IO::Socket::SSL->new(Proto => "tcp", + SSL_verify_mode => 0x00, + PeerAddr => $host, + PeerPort => $port, + ) + or error("Can't connect to port $port on $host: $!",0), return 0; + } else { + $handle = IO::Socket::INET->new(Proto => "tcp", + PeerAddr => $host, + PeerPort => $port, + ) + or error("Can't connect to port $port on $host: $!",0), return 0; + } + $handle->autoflush(1); # So output gets there right away. + Irssi::print("...done"); + receive(); + alarm 0; + }; + if ($@) { + alarm 0; + if ($@ =~ /timeout/) { + alarm(); + return 0; + } else { + error("$@",0); + return 0; + } + } + return 1; +} + +# +# Subroutine to login to the mailbox. +# +sub login { + my ($response,$success); + my ($user,$password); + + + $user = Irssi::settings_get_str('mailcheck_imap_user'); + $password = Irssi::settings_get_str('mailcheck_imap_password'); + + + $logged_in = 0; + # Set an alarm in case we can not connect or get hung. Older versions + # the IO::Socket perl module caused errors with the alarm we set before + # setting up the socket. If this program dies in debug mode saying: + # "Alarm clock", then you can probably fix it by upgrading your perl + # IO module. + eval { + alarm 30; + send_data("A001 LOGIN \"$user\" \"$password\"","\"$user\""); + while (1) { + ($success,$response) = receive(); + if (! $success) { + return 0; + } + last if $response =~ /LOGIN|OK/; + } + if ($response =~ /fail|BAD/) { + return 0; + } else { + $logged_in = 1; + } + alarm 0; + }; + if ($@) { + alarm 0; + if ($@ =~ /timeout/) { + alarm(); + return 0; + } else { + error("$@",0); + return 0; + } + } + # Success! :D + return 1; +} + +# +# Subroutine that does check of imap mailbox. +# +sub check_imap { + my ($type) = @_; + + #my ($type) = ("MESSAGES"); + + my ($response,$success,$num_messages); + # Set an alarm in case we can not connect or get hung. Older versions + # the IO::Socket perl module caused errors with the alarm we set before + # setting up the socket. If this program dies in debug mode saying: + # "Alarm clock", then you can probably fix it by upgrading your perl + # IO module. + eval { + alarm 30; + send_data("A003 STATUS INBOX ($type)"); + while (1) { + ($success,$response) = receive(); + if (! $success) { + return "-1"; + } + last if $response =~ /STATUS\s+.*?\s+\($type/; + } + ($num_messages) = $response =~ /\($type\s+(\d+)\)/; + alarm 0; + }; + if ($@) { + alarm 0; + if ($@ =~ /timeout/) { + alarm(); + return "-1"; + } else { + error("$@",0); + return "-1"; + } + } + return $num_messages; +} + + +# +# Subroutine to send a line to the imap server. +# Block everything after $block. +# +sub send_data { + my ($line,$block) = (@_); + print $handle "$line\r\n"; + $line =~ s/(.*$block).*/$1 ----/ if ($block); + Irssi::print("sent: $line") if Irssi::settings_get_bool('mailcheck_imap_debug'); + return 1; +} + + +# +# Subroutine to get a response from the imap server and print. +# that response if in debug mode. +# +sub receive { + my ($response,$success); + $response = ""; + $success = 0; + chomp($response = <$handle>); + if ($response) { + Irssi::print("got: $response") if Irssi::settings_get_bool('mailcheck_imap_debug'); + $success = 1; + } else { + Irssi::print("no response!") if Irssi::settings_get_bool('mailcheck_imap_debug'); + } + return ($success,$response); +} + +# +# Subroutine to display and error message in a text box. +# +sub error { + my ($error,$fatal) = (@_); + + if ($fatal) { + # TODO : Print some useful message and die? + Irssi::print("mailcheck_imap FATAL : $error"); + return 0; + } else { + Irssi::print("mailcheck_imap error : $error"); + + if ($refresh_tag) { + Irssi::timeout_remove($refresh_tag) + } + my $time = Irssi::settings_get_int('mailcheck_imap_interval'); + $refresh_tag = Irssi::timeout_add($time*1000, 'refresh_mailcheck_imap', undef); + $handle = 0; + return 0; + } +} + +# +# Subroutine to call when alarm times out. +# +sub alarm { + Irssi::print("Alarm went off!") if Irssi::settings_get_bool('mailcheck_imap_debug'); + return 1; +} + + +# +# Subroutine to clean up. +# +sub cleanup { + if ($handle) { + send_data("A999 LOGOUT"); + $handle->close(); + } + Irssi::print("mailcheck_imap logged out."); +} + + + +####################################################################### +# Simply requests a statusbar item redraw. + +sub update_statusbar_item { + Irssi::statusbar_items_redraw('mailcheck_imap'); +} + + +####################################################################### +# This is the function called by irssi to obtain the statusbar item. + +sub mailcheck_imap { + my ($item, $get_size_only) = @_; + + my $theme = Irssi::current_theme(); + my $format = $theme->format_expand("{sb_mailcheck_imap}"); + + if ($format) { + # use theme-specific look + $format = $theme->format_expand("{sb_mailcheck_imap $new_messages $total_messages}", Irssi::EXPAND_FLAG_IGNORE_REPLACES); + } else { + # use the default look + $format = "{sb Mail: ".$new_messages." new, ".$total_messages." total}"; + } + + if($new_messages == 0) { + if(Irssi::settings_get_bool('mailcheck_imap_show_zero')) { + $format = $theme->format_expand("{sb_mailcheck_imap_zero $new_messages $total_messages}", Irssi::EXPAND_FLAG_IGNORE_REPLACES); + + if (!$format) { + # use the default look + $format = "{sb Mail: None new, ".$total_messages." total}"; + } + } else { + $format = ""; + } + } + + if (length($format) == 0) { + # nothing to print, so don't print at all + if ($get_size_only) { + $item->{min_size} = $item->{max_size} = 0; + } + } else { + $item->default_handler($get_size_only, $format, undef, 1); + } +} + + +################################################################################ +# Ensure that all required details are filled in: +# host, user, password +sub check_details { + my $host = Irssi::settings_get_str('mailcheck_imap_host'); + my $user = Irssi::settings_get_str('mailcheck_imap_user'); + my $password = Irssi::settings_get_str('mailcheck_imap_password'); + + if(!$host || !$user || !$password) { + show_help(); + return 0; + } + return 1; +} + + +################################################################################ +# Immediately check for new mail (updates statusbar item too) + +sub cmd_mailcheck_imap { + refresh_mailcheck_imap(); +} + + +################################################################################ +# Kill the connection and stop the refresh. +sub cmd_mailcheck_imap_stop { + if ($refresh_tag) { + Irssi::timeout_remove($refresh_tag); + } + cleanup(); +} + +# Also close connection on script unload? +sub sig_command_script_unload ($$$) { + my ($script, $server, $witem) = @_; + + if($script =~ /^mailcheck_imap\.pl$/ || + $script =~ /^mailcheck_imap/) { + cleanup(); + } +} + +Irssi::signal_add_first('command script unload', \&sig_command_script_unload); + + +####################################################################### +# Adding stuff to irssi + +Irssi::settings_add_int('mail', 'mailcheck_imap_interval', 120); +Irssi::settings_add_bool('mail', 'mailcheck_imap_use_ssl', 0); +Irssi::settings_add_bool('mail', 'mailcheck_imap_debug', 0); +Irssi::settings_add_bool('mail', 'mailcheck_imap_show_zero', 0); +Irssi::settings_add_bool('mail', 'mailcheck_imap_echo_new_in_window', 1); + +Irssi::settings_add_str('mail', 'mailcheck_imap_host', ''); +Irssi::settings_add_int('mail', 'mailcheck_imap_port', 143); +Irssi::settings_add_str('mail', 'mailcheck_imap_user', ''); +Irssi::settings_add_str('mail', 'mailcheck_imap_password', ''); + + +Irssi::statusbar_item_register('mailcheck_imap', '{sb $0-}', 'mailcheck_imap'); + +Irssi::command_bind('mailcheck_imap_help','cmd_mailcheck_imap_help'); +Irssi::command_bind('mailcheck_imap','cmd_mailcheck_imap'); +Irssi::command_bind('mailcheck_imap_stop','cmd_mailcheck_imap_stop'); + + +####################################################################### +# Startup functions + +# Check that everything is fiiiine and start checking if so +if(check_details()) { + # All is ok, so start running it + refresh_mailcheck_imap(); + update_statusbar_item(); +} + + +####################################################################### |