Since there has been some interest in using IMAP IDLE, Perl and Snarl, I wrote a cleaner more useful proof of concept that I have named idlemailcheck. Since I’ve never used Google Code, I decided to try it out and host the code there: idlemailcheck @ Google Code.
The code is very raw and could use a improvement. It is very much a proof of concept and run it at your own risk. To see what it looks like in action, I’ve included a few screenshots of the Snarl notifications that appear over there on the left. The very nice icon comes from a Jonas Rask Design, and is free for personal use. This version does not play a sound, but subsequent versions will make that an option. I cannot stress enough that this is a proof of concept and that expectations should be low in this every version.
With that said, the script actually works fine in my limited testing. I don’t have a long term test record of it, but that will come with time. Before running the script you’ll need a few things installed.
You’ll also need to set the IMAP server address (the IMAP server must support IDLE) as well as your username and password. The path to the icon also needs to be set in the script. It does output debug information on the command line, so if something goes wrong, check your terminal window. I’ve had success running the script in Cygwin, but running the script within ActiveState’s ActivePerl should also be fine.
With that, I give you idlemailcheck v0.0.1, released under the GPL v2. You can view the code below, but if you’re looking to play with it, be sure to download the code and icon from the download page.
#!/usr/bin/env perl
#######################################
#
# idlemailcheck - IMAP IDLE mail check with Snarl notification
#
# Copyright (C) Justin Ribeiro
# Project Page - http://www.justinribeiro.com/
#
# 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 2 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
# USA
#
# 08/28/2009
# JDR - Initial Concept Release 0.0.1
#
# The script logs into an IMAP server of your choice (which must support
# the IDLE command), sits at IDLE until the mail server has a new email, and
# then returns a Snarl notification when new mail arrives.
#
# This connect portion of the script is based on the Gmail connect script at Perl Monks
# that was written by polettix and refined by markov: http://www.perlmonks.org/?node_id=649742
#
# Requirements
# 1. Snarl for Windows (http://www.fullphat.net/)
# 2. Win32::Snarl
# 3. IO::Socket::SSL
# 4. Mail::IMAPClient
#
# You must set the variables below for your specific IMAP server. If you want the icon
# to appear, make sure you update to the full path as in the example below.
#
# Tested with:
#
# * Gmail
# * Google Apps for Domain
#
# Feel free to make changes and additions; I'm open to suggestions, fixes, and just
# plain better ways to do things.
#
# Released under the GPL License. Use at your own risk.
#
#######################################
use warnings;
use strict;
use Mail::IMAPClient;
use IO::Socket::SSL;
use Win32::Snarl;
#### SETUP - YOU MUST SET THE FOLLOWING OR NOTHING WILL WORK
my $IMAP_server = "imap.gmail.com";
my $IMAP_port = "993";
my $IMAP_user = "";
my $IMAP_password = "";
my $SNARL_disptime = 7; # number of seconds to display snarls alerts
# path to icon
# icon from Jonas Rask Design http://jonasraskdesign.com/ (free for personal use)
# if you use a differnt icon, it must be 128x128 png
my $use_icon = "C:\\path\\to\\stamp.png";
#### EDITING BELOW AT OWN RISK
# Connect to the IMAP server via SSL
my $socket = IO::Socket::SSL->new(
PeerAddr => $IMAP_server,
PeerPort => $IMAP_port,
)
or die "socket(): $@";
# Build up a client attached to the SSL socket.
# Login is automatic as usual when we provide User and Password
my $client = Mail::IMAPClient->new(
Socket => $socket,
User => $IMAP_user,
Password => $IMAP_password,
)
or die "new(): $@";
# we set Uid to false so that we can use the message sequence number to email information
$client->Uid(0);
# need these for later
my $noidle = 0;
my $last = 0;
my $buf;
my $bytes_read;
my $my_ID;
my $msg_count;
# Do something just to see that it's all ok
if ($client->IsAuthenticated())
{
my $winAuth = Win32::Snarl::ShowMessage('Gmail Authenticated', 'You are now logged into Gmail.', $SNARL_disptime, $use_icon);
print "Log in successful.
";
# open inbox folder
$client->select("INBOX");
# idle the connection
my $idle = $client->idle or warn "Couldn't idle: $@
";
print "IMAP now idle....waiting for email
";
# we sit an wait for the service to return some data
while($bytes_read = $socket->sysread($buf, 4096))
{
# looks like we have something
print "Read $bytes_read bytes from the socket...
";
# what's in the buffer
print "Data:
" . $buf . "
";
# grab one line at a time from the buffer
while ( $buf =~ s/^(.*?\R)//o )
{
# get the current buffer line
my $current_line = $1;
# looking for a valid respone
if ( $current_line !~ s/\s*\{(\d+)\}\R$//o )
{
# we only want "* XXX EXISTS" commands from the IMAP server, where XXX is the message sequence number
# $last != 1 is to make sure this isn't the last EXISTS command after an EXPUNGE; not a new message senario
if ( $current_line =~ s/EXISTS//g && $last != 1)
{
# we have to end the IDLE before we can get message information, otherwise the IMAP server will get angry
$client->done($idle) or warn "Error from done: $@
";
#check the unseen_count; this is a precaution, because for some reason Gmail at times responds with a new message notifcation, but's it's really not.
$msg_count = $client->unseen_count||0;
if ($msg_count > 0)
{
# grab the message sequence number from the string
$current_line =~ s/\D//g;
$my_ID = $current_line;
print "MID = " . $my_ID . "
";
# Email Data: Sender's address
my $from = $client->get_header($my_ID, "From");
$from =~ s/<[^>]*>//g; #strip the email, only display the sender's name
# Email Data: Subject
my $subject = $client->get_header($my_ID, "Subject");
# Send event to Snarl
my $winNM = Win32::Snarl::ShowMessage($from, $subject, $SNARL_disptime, $use_icon);
# make sure we keep the seen flag unset so the message stays unread
$client->unset_flag("Seen",$my_ID) or die "$0: Could not unset_flag: $@
"; #
# restart the IDLE
$idle = $client->idle or warn "Couldn't idle: $@
";
print "IMAP now idle....waiting for email
";
next;
}
else
{
$idle = $client->idle or warn "Couldn't idle: $@
";
print "IMAP now idle....waiting for email
";
}
}
else
{
# we had something other than EXISTS
$last = 1;
}
}
}
$last = 0;
}
# kill the IDLE
$client->done($idle) or warn "Error from done: $@
";
# Say bye
$client->logout();
}
else
{
# ahh !@#$.
print "Login failed.";
}