Skip to main content

If you don't have an IBM ID and password, register here.

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

Cultured Perl: Use IMAP with Perl

An IMAP client built with the Mail::IMAPClient CPAN module

Teodor Zlatanov (tzz@iglou.com), Programmer, Gold Software Systems
Teodor Zlatanov graduated with an M.S. in computer engineering from Boston University in 1999. He has worked as a programmer since 1992, using Perl, Java, C, and C++. His interests are in open source work on text parsing, three-tier client-server database architectures, UNIX system administration, CORBA, and project management.

Summary:  Ted introduces you to accessing IMAP with the Mail::IMAPClient CPAN module. Using Mail::IMAPClient, he built the ifrom utility as an alternative to other IMAP and POP3 mail checkers. ifrom has its own unique advantages that can be used for listing, printing, and moving messages on an IMAP server, and backing up IMAP mail.

Date:  19 Jun 2003
Level:  Intermediate

Comments:  

IMAP (Internet Message Access Protocol) and its popular cousin, POP3 (the Post Office Protocol), are both very widely implemented protocols that allow access to e-mail. Although POP is ubiquitous, IMAP is the better choice for handling mail in many cases. For example, POP3's single mailbox model can lead to thousands of large messages in a single location, making access and control of the mailbox a tedious affair. IMAP, however, is designed specifically for leaving the messages on the server, classified in various mailboxes. Additionally, IMAP can check for new mail over an existing IMAP connection, while POP3 has to open a new connection every time. (For links to technical details on both protocols, check the Resources section of this article. The POP3 RFC has specific information on connections in the "Scaling and Operational Considerations" section.)

In this article I'll focus on IMAP and present my ifrom utility for managing your e-mail (see Resources to download ifrom.pl). Personally, I use ifrom for listing, printing, and moving messages on an IMAP server. I also run it nightly for backing up my IMAP mail, because I'm paranoid about it. (And remember, just because you're paranoid doesn't mean they're not after you.)

Listing messages

The simplest invocation of ifrom is just so, without any options. It will access your INBOX mailbox on the IMAP server and list your messages in a numbered list. The -host option tells ifrom which IMAP server to contact.

Note that ifrom uses the AppConfig module, which provides for default values, among other nice features.

Authentication in ifrom is done with plain LOGIN (meaning you specify your password and user name in plain text to the IMAP server). Follow the link to the Authmechanism capability of the Mail::IMAPClient package in Resources. You can use CRAM-MD5 authentication with the -crammd5 switch; no other mechanisms are supported by Mail::IMAPClient, so I did not bother to set up a generic -authentication switch. Note that you can implement your own authentication; see the Mail::IMAPClient documentation.


Listing 1. Authenticating to the IMAP server
if ($config->CRAMMD5())
{
 my $authmech = "CRAM-MD5";
 if ($imap->has_capability($authmech))
 {
  print "Switching to $authmech authentication\n";
  $imap->Authmechanism($authmech);
 }
}

You can specify your name and password from the command line with the -user and -password switches, or through an authinfo (AKA netrc) file. The authinfo file stores a list of authentication options, such as:


Listing 2. Authinfo file format for ifrom
machine imap.yourserver.here login joe password JoeSecret
machine imap.yourserver.there password FredSecret port 244

The authinfo file is used by other programs, and the format is variable depending on what those programs need; ifrom parses it looking for the machine, login, password, and port keywords. If any of those keywords are not specified, the defaults are used. The authinfo file overrides the -user and -password command-line switches. Make sure your authinfo file is only readable by you!

The machine name is your IMAP server's name. The machine name in the authinfo file has to match exactly the name given to ifrom with the -host switch.

When ifrom connects to the server, it sets the Peek variable to 1, so that messages we examine are not marked read.

Next, ifrom opens the mailbox specified by the -mailbox switch. This is INBOX by default: the standard IMAP main mailbox.

After all this trouble, ifrom prints out messages using this format:


Listing 3. Printf a message header
printf "%5d %-35.35s %s\n", $count, $address,
   ((defined $data->{Subject}->[0]) ? $data->{Subject}->[0] : '');

The sender's address in $address is extracted from the full user name whenever possible, using a simple regular expression match.

If you give ifrom the -dump switch, it will also print the message's contents after the header. This is very useful if you don't have time to launch your e-mail application to see the contents of a last-minute message. It's okay to interrupt ifrom while it's printing a large message; the message will not be deleted or otherwise affected, since we opened the server with the Peek option set to 1.

Note that the -dump switch uses the body_string() function, whereas the -backup option we'll see later uses the message_string() function.


Moving messages

Occasionally, you want to move messages from one mailbox to another. If there are many messages and your mail client loses the connection to the IMAP server while moving them (as happens often over a slow network link), this capability is for you.

Just use the -mailbox and -to options. It's that simple. Run ifrom like this:


Listing 4. Moving messages with ifrom
ifrom -mailbox newmail -to archive

You can also tell ifrom to stop moving messages after a certain number with the -n switch.

Here is the actual code that does the moving. IMAP has built-in article moving, so the client simply has to invoke the move() function.


Listing 5. How the messages get moved by Perl
 foreach my $message (@msg_list)
 {
  $count++;
  if ($config->TO)
  {
   die "Could not move message $message: $!" unless $imap->move($config->TO, $message);
   print "Moved message $message to " . $config->TO, "\n";
   $imap->expunge() if $config->EXPUNGE_OFTEN;
   last if $count >= $config->N;
  }
 }

 if ($config->TO)
 {
  $imap->expunge();
 }

Moving messages in IMAP does not respect the Peek setting. Even if it is set to 1, Peek is only relevant to looking at messages. Moving the messages will always delete their originals after the copies have been made in the destination mailbox, as implemented by Mail::IMAPClient.


Expunging and deleting mailboxes

When moving messages above, you must have noticed the mysterious expunge() function. It simply tells the IMAP server to flush the mailbox, removing all messages marked as deleted. Normally, when listing messages, we don't mark them as deleted because we have Peek set to 1. The move() function, however, will mark them as deleted as mentioned above. In order to really delete those messages, the expunge() function must be called.

This wouldn't be necessary if IMAP really moved messages, but it doesn't, so Mail::IMAPClient implements move() as a copy() followed by a deletion.

Why is this useful? We all delete mail accidentally. This IMAP feature allows you to get deleted messages back before the final expunge is done.

The -expunge_often flag is for moving messages when you are on an unreliable link and could get disconnected any time. It ensures that after every message is moved, expunge() will be called (otherwise, the message will remain in the mailbox). It is better, however, to use plain expunge() after all the messages are moved, in combination with the -n flag. That way, expunge() will be called after every 10, 15, or however many messages you specify, are moved.

I also gave ifrom the -delete_mailbox_really option. When given that option, ifrom will delete whatever mailbox is named by the -mailbox switch, so don't use it on the default INBOX mailbox! The Perl code involved couldn't be simpler:


Listing 6. Deleting a mailbox
if ($config->DELETE_MAILBOX_REALLY)
{
 $imap->delete($config->MAILBOX)
  or warn "Could not delete mailbox " . $config->MAILBOX . "\n";
}


Backing up IMAP mail

I like backups; they have saved my work many, many times. When it comes to e-mail, backups may seem less important, but in fact many of our daily interactions fly by over e-mail. Often, we find out much later that we shouldn't have erased that memo about wearing shorts on Fridays, or the reminder about the company-wide meeting a month from now. If you aren't careful, the overworked assistant you ask to track down a lost memo will tell your boss you've been hoarding cocoa packets in your desk. Don't let this happen to you.

To back up your mail using ifrom, just give it the -backup flag. The -savedir switch is also important -- set it to wherever you want the saved messages to go. Everything else -- authentication, host, port, and so on -- works just like the regular ifrom. The -mailbox parameter doesn't work, because ifrom backs up all the mailboxes. I could have had a special -backup_mailbox flag to override "all mailboxes," but frankly I never needed it.

Messages in IMAP mailboxes have unique numbers. We take advantage of that fact in ifrom, saving each message to the file savedir/mailbox/messageNumber and skipping the message if the file already exists.


Listing 7. Backing up messages
  foreach my $message (@msg_list)
  {
   my $filename = "$dir/$f/$message";
   next if -e $filename;
   print "saving message $f/$message to $filename\n" if $config->VERBOSE;
   my $data_fh = new IO::File $filename, "w";
   my $data = $imap->message_string($message);
   warn "Empty message data for $f/$message" unless defined $data && length $data;
   $data_fh->print($data);
  }

We use the message_string() function because it retrieves the whole message. The body_string() function used by the -dump switch earlier skips the messages headers, which are usually not interesting when you're looking at e-mail quickly.


Conclusion

The ifrom utility is one of those little scripts that just kept growing and gaining useful features. I wouldn't be surprised if other people have done their own ifrom scripts. The basic message-listing feature has been incredibly useful to me, since checking my IMAP mail is sometimes impossible if I'm doing system maintenance in the machine room, or I'm on a slow connection.

I have already used the ifrom backups several times, when I've lost important mail by accident. It's a load off my mind to know that even if the ISP that runs my IMAP server should lose my mailboxes (it's happened to me before), I will lose at most one day's worth of mail.

I hope this article has interested you in writing Perl to interface with IMAP, and that you find ifrom useful. If you use ifrom, I'd love to hear how you use it.



Download

NameSizeDownload method
l-cpimap/ifrom.plHTTP

Information about download methods


Resources

About the author

Teodor Zlatanov graduated with an M.S. in computer engineering from Boston University in 1999. He has worked as a programmer since 1992, using Perl, Java, C, and C++. His interests are in open source work on text parsing, three-tier client-server database architectures, UNIX system administration, CORBA, and project management.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in

If you don't have an IBM ID and password, register here.


Forgot your IBM ID?


Forgot your password?
Change your password


By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. This profile includes the first name, last name, and display name you identified when you registered with developerWorks. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)


By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux
ArticleID=11319
ArticleTitle=Cultured Perl: Use IMAP with Perl
publish-date=06192003
author1-email=tzz@iglou.com
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).