Monday, November 2, 2009

The IMAP Protocol
























Network
Programming with Perl
By
Lincoln D. Stein
Slots : 1
Table
of Contents
Chapter 8.  POP, IMAP,
and NNTP






    Content







The IMAP Protocol


The POP3 protocol was designed to handle the
case of a user who spends most of his or her time working on a
single machine. The mail client's job is to fetch the user's
unread mail from time to time from the remote mailbox server.
The user then reads the mail and possibly sorts it into
several local mail folders.


Keeping track of mail becomes more
complicated, however, when the user is moving around a lot:
working on a desktop in the office, a laptop while traveling,
and another desktop at home. In this case, the user wants to
see the same set of mail files no matter where he or she
happens to be working. The Internet Message Access Protocol
(IMAP) satisfies these needs by managing multiple remote mail
folders and transparently synchronizing them with local
copies, providing the user with a consistent view of stored
e-mail. IMAP clients also provide the user with the ability to
work off-line, and with sophisticated server-side message
search functions.


Unfortunately, the IMAP protocol is also
rather complex and it does certain things that the simple
request/response model of Net::POP3 can't easily handle. Among
other things, IMAP servers send unsolicited messages to the
client from time to time, for example to alert the client that
new mail has arrived. No fewer than three Perl modules on CPAN
deal with IMAP: Mail::IMAPClient, Net::IMAP, and
Net::IMAP::Simple.


Mail::IMAPClient, written by David Kernen,
provides the most functionality of the three, providing
methods for issuing all of the IMAP commands. However,
Mail::IMAPClient does not do such a good job at mapping the
IMAP server's responses onto easily handled Perl objects. To
use this module, you'll need RFC 2060 on hand and be prepared
to parse the server responses yourself.


Net::IMAP, written by Kevin Johnson, does a
better job at handling the server's responses, and provides a
nifty callback interface that allows you to intercept and
handle server events. Unfortunately, the module is in alpha
stage and the interfaces are changing. Also, at the time this
book was written, the module's documentation was
incomplete.


Currently, the most usable interface to IMAP
is Joao Fonseca's Net::IMAP::Simple, which provides access to
the subset of IMAP that is most like POP3. In fact,
Net::IMAP::Simple shares much of Net::POP3's method interface
and is, to a large extent, plug compatible.


Like Net::POP3, you work with
Net::IMAP::Simple by calling its new(), method to
connect to an IMAP server host, authenticate with
login(), list messages with list() and
top(), and retrieve messages with get().
Unlike Net::POP3, Net::IMAP::Simple has no apop()
method for authenticating without plaintext passwords. To make
up for this deficiency, it has the ability to work with
multiple remote mailboxes. Net::IMAP::Simple can list the
user's mailboxes, create and delete them, and copy messages
from one folder to another.


Summarizing an IMAP Mailbox


The class=docEmphasis>pop_stats.pl program from Figure
8.1 summarizes the contents of a POP3 mailbox. We'll now
enhance this program to summarize an IMAP mailbox. As an added
feature, the new script, named class=docEmphasis>imap_stats.pl, indicates whether a
message has been read. You call it like class=docEmphasis>pop_stats.pl, but with an additional
optional command-line argument that indicates the name of the
mailbox to summarize:

% pop_stats.pl lstein@localhoszt gd_bug_reports
lstein@localhost password:
gd has 6 messages (2 new)
1 Honza Pazdziora Re: ANNOUNCE: GD::Latin2 patch (fwd) read
2 Gurusamy Sarathy Re: patches for GD by Gurusamy Sarathy read
3 Honza Pazdziora Re: ANNOUNCE: GD::Latin2 patch (fwd) read
4 Erik Bertelsen GD-1.18, 2 minor typos read
5 Erik Bertelsen GD fails om some GIF's unread
6 Honza Pazdziora GDlib version 1.3 unread

Figure
8.5 lists class=docEmphasis>imap_stats.pl.



Figure 8.5.
Summarize an IMAP mailbox




Lines 1�5: Load modules We load
Net::IMAP::Simple, Mail::Header, and the Prompt Util module
used in earlier examples.




Lines 6�9: Process command-line
arguments
We parse out the username and mailbox host
from the first command-line argument, and recover the
mailbox name from the second. If no mailbox name is
provided, we default to class=docEmphasis>INBOX, which is the default mailbox
name on many UNIX systems. We then prompt for the user's
password.




Lines 10�14: Connect to remote host
We call the Net::IMAP::Simple->new(), method to
connect to the designated host, and then call
login() to authenticate. If these steps are
successful, we call the object's select() method to
select the indicated mailbox. This call returns the total
number of messages in the mailbox, or if the mailbox is
empty or missing, undef. We fetch the number of the
last message read by calling last().




Lines 15�24: List contents of the
mailbox
We loop through each of the messages from first
to last. For each one, we fetch the header by calling
top(), parse it into a Mail::Header object, and
retrieve the Subject: and From: fields. We also call the
IMAP object's seen() method to determine whether
the message has been retrieved. We then print the message
number, sender, subject line, and read status.




Lines 26�32: clean_from()
subroutine This is the same subroutine we saw in the earlier
version of this program. It cleans up the sender addresses.



The Net::IMAP::Simple API


Although Net::IMAP::Simple is very similar to
Net::POP3, there are some important differences. The most
dramatic difference is that Net::IMAP::Simple does not inherit
from Net::Cmd and, therefore, does not implement the
message() or code() methods. Furthermore,
Net::IMAP::Simple is not a subclass of IO::Socket and,
therefore, cannot be treated like a filehandle.


The new() and login()
methods are similar to Net::POP3:







$imap =
Net::IMAP::Simple->new($host [,$opt1=>$val1,
$opt2=>$val2�])


The new() method constructs a
new Net::IMAP::Simple object. The first argument is the
name of the host, and is not optional (unlike the
Net::POP3 equivalent). This is followed by a series of
options that are passed directly to
IO::Socket::INET.


If unsuccessful, new() returns
undef and $! is set to some error
code. Otherwise, it returns a Net::IMAP::Simple object
connected to the server.


$messages =
$imap->login($username,$password)


The login() method attempts to
log into the server using the provided username and
password. The username and password are required, also a
departure from Net::POP3. If successful, the method
returns the number of messages in the user's default
mailbox, normally INBOX. Otherwise, login()
returns undef.


Note that login() does class=docEmphasis>not return 0E0 for a
default mailbox that happens to be empty. The correct
test for a successful login is to test for a defined
return value.


Several functions provide access to
mailboxes.







@mailboxes =
$imap->mailboxes


The mailboxes() method returns
a list of all the user's mailboxes.


$messages =
$imap->select($mailbox)


The select() method selects a
mailbox by name, making it current. If the mailbox
exists, select() returns the number of messages
it contains (0 for a mailbox that happens to be empty).
If the mailbox does not exist, the method returns
undef and the current mailbox is not
changed.


$success =
$imap->create_mailbox($mailbox)


$success =
$imap->delete_mailbox($mailbox)


$success =
$imap->rename_mailbox($old_name,$new_name)


The create_mailbox(),
delete_mailbox(), and rename_mailbox()
methods attempt to create, delete, and rename the named
mailbox, respectively. They return true if successful,
and false otherwise.


Once you have selected a mailbox, you can
examine and retrieve its contents.







$last_msgnum
= $imap->last


The last() method returns the
highest number of the read messages in the current
mailbox, just as Net::POP3 does. You can also get this
information by calling the seen() method, as
described below.


$arrayref =
$imap->get($msgnum)


The get() method retrieves the
message indicated by the provided message number from
the current mailbox. The return value is a reference to
an array containing the message lines.


$handle =
$imap->getfh($msgnum)


This is similar to get() but
the return value is a filehandle that can be read from
in order to retrieve the indicated message. This method
differs from the similarly named Net::POP3 method by
returning a filehandle opened on a temporary file,
rather than a tied filehandle. This means that the
entire message is transferred from the remote server to
the local machine behind the scenes before you can begin
to ork with it.


$flag =
$imap->delete($msgnum)


The delete() method marks the
indicated message for deletion from the current mailbox.
Marked messages are not removed until the
quit() method is called. However, there is no
reset() call to undo a deletion.


$arrayref =
$imap->top($msgnum)


The top() method returns the
header of the indicated message as a reference to an
array of lines. This format is suitable for passing to
the Mail::Header->new() method. There is no
option for fetching a certain number of lines from the
body text.


$hashref =
$imap->list


$size =
$imap->list($msgnum)


The list() method returns
information on the size of mailbox messages. Called
without arguments, it returns a hash reference in which
the keys are message IDs, and the values are the sizes
of the messages, in bytes. Called with a message ID, the
method returns the size of the indicated message, or if
an invalid message number was provided, it returns
undef.


$flag =
$imap->seen($msgnum)


The seen() method returns true
if the indicated message has been read (by calling the
get() method), or false if it has not.


$success =
$imap->copy($msgnum,$mailbox_destination)


The copy() method attempts to
copy the indicated message from the current mailbox to
the indicated destination mailbox. If successful, the
method returns a true value and the indicated message is
appended to the end of its destination. You may wish to
call delete() to remove the message from its
original mailbox.


When you are finished, the quit()
method will clean up:







class=docEmphStrong>$imap->quit()


quit() takes no arguments. It
deletes all marked messages and logs
off.









       




    Top

    No comments:

    Post a Comment