I recently caught a glimpse of how Gmail Notifier works on a friend’s Mac. It looked pretty cool. Unfortunately for me, though, there’s no reasonable facsimile in Linux. Sure, there are a couple options, but they aren’t available in Gentoo’s package management system. Given my recent experience dealing with E-mail from Perl, I figured it would be just as easy to write my own E-mail notifier as it would be to manually install these programs (along with their dependencies). I was right. I just spent the last ~20 minutes (while idling through a meeting) writing such an app. The code follows below. Its only dependency is XOSD.
Disclaimer: I blatantly cribbed some of my code from Flavio Poletti (for the MTA stuff) and Bill Luebkert (for the password input).
Future work: right now the code simply polls the mail server once every three minutes. In the future I’ll post an update that uses IMAP Idle to reduce bandwidth.
#!/usr/bin/perl -w
use Term::ReadKey; END { ReadMode ('restore'); } # just in case
use Mail::IMAPClient;
use IO::Socket::SSL;
use File::HomeDir;
my $username = '[email protected]';
my $sleeptime = 180; # Time between checks, in seconds.
my $conffile = File::HomeDir->my_home . "/.checkmail";
######################################################
$canceled = 0;
$inwhile = 0;
sub get_passwd {
# legal clear passwd chrs (26+26+10+24=86): "a-zA-Z0-9!#$%&()*+,-./:;<=> ?@[\]^";
my @legal_clear = ('a'..'z', 'A'..'Z', '0'..'9', split //,
'!#$%&()*+,-./:;<=> ?@[\]^');
my %legal_clear; foreach (@legal_clear) { $legal_clear{$_} = 1; }
$| = 1; # unbuffer stdout to force unterminated line out
ReadMode ('cbreak');
my $ch = '';
while (defined ($ch = ReadKey ())) {
last if $ch eq "\x0D" or $ch eq "\x0A";
if ($ch eq "\x08") { # backspace
print "\b \b" if $passwd; # back up 1
chop $passwd;
next;
}
if ($ch eq "\x15") { # ^U
print "\b \b" x length $passwd; # back 1 for each char
$passwd = '';
next;
}
if (not exists $legal_clear{$ch}) {
print "\n'$ch' not a legal password character\n";
print 'Password: ';
next;
}
$passwd .= $ch;
}
print "\n";
ReadMode ('restore');
return $passwd;
}
$SIG{'INT'} = 'INT_handler';
sub INT_handler {
exit(0) if(!$inwhile);
$canceled = 1;
print "\nCaught Signal; exiting gracefully!\n";
}
print "Password: ";
my $password = &get_passwd();
while(!$canceled) {
$inwhile = 1;
my $socket = IO::Socket::SSL->new(
PeerAddr => 'imap.gmail.com',
PeerPort => 993,
)
or (print STDERR "Warning: lost internet connection!\n" && next); # Perhaps we lost the internet connection?
my $greeting = <$socket>;
my ($id, $answer) = split /\s+/, $greeting;
die "problems logging in: $greeting" if $answer ne 'OK';
my $client = Mail::IMAPClient->new(
Socket => $socket,
User => $username,
Password => $password,
Uid => 1,
)
or die "new(): $@";
$client->State(Mail::IMAPClient::Connected());
$client->login() or die 'login(): ' . $client->LastError();
die("Failed authentication!\n") unless $client->IsAuthenticated();
$client->examine('INBOX') or die "Could not examine: $@\n";
my @msgs = $client->unseen or die "Could not search the inbox! $@\n";
my $last_max = -2;
if(-e $conffile) {
# Load the old largest
open(CONFFILE, "<" . $conffile) or die("Error opening " . $conffile . "\n");
while() {
my $line = $_;
$last_max = $1 if($line =~ /^\s*last_max_uid\s*=\s*(\d+)\s*$/i);
}
close(CONFFILE);
}
my $max = -1;
my @over;
for my $msg (@msgs) {
$max = $msg if $msg > $max;
push(@over, $msg) if $msg > $last_max;
}
if($max >= 0) {
open(CONFFILE, ">" . $conffile) or die("Error opening $conffile for writing!\n");
print CONFFILE "last_max_uid = " . $max . "\n";
close(CONFFILE);
}
if($last_max >= 0) {
open(OSDC, "| osd_cat -c green -p middle -A center -s 2 -l 5 -f \"-bitstream-bitstream vera serif-*-*-*-*-17-*-*-*-*-*-*-*\"");
for my $m (@over) {
my $hashref = $client->parse_headers($m, "From")
or die "Could not parse_headers: $@\n";
print OSDC "New mail from " . $hashref->{"From"}->[0] . "!\n";
}
close(OSDC);
}
$client->logout();
sleep $sleeptime;
}
PoC‖GTFO
Twitter
LinkedIn
GitHub
XTerm