=head1 NAME Mail::IMAPClient - An IMAP Client API =head1 DESCRIPTION This module provides methods implementing the IMAP protocol. It allows perl scripts to interact with IMAP message stores. The module is used by constructing or instantiating a new IMAPClient object via the L constructor method. Once the object has been instantiated, the L method is either implicitly or explicitly called. At that point methods are available that implement the IMAP client commands as specified in I. When processing is complete, the I object method should be called. This documentation is not meant to be a replacement for RFC2060, and the wily programmer will have a copy of that document handy when coding IMAP clients. Note that this documentation uses the term I in place of RFC2060's use of I. This documentation reserves the use of the term I to refer to the set of folders owned by a specific IMAP id. RFC2060 defines four possible states for an IMAP connection: not authenticated, authenticated, selected, and logged out. These correspond to the B constants C, C, C, and C, respectively. These constants are implemented as class methods, and can be used in conjunction with the L method to determine the status of an B object and its underlying IMAP session. Note that an B object can be in the C state both before a server connection is made and after it has ended. This differs slightly from RFC2060, which does not define a pre-connection status. For a discussion of the methods available for examining the B object's status, see the section labeled L<"Status Methods">, below. =head2 Advanced Authentication Mechanisms RFC2060 defines two commands for authenticating to an IMAP server: LOGIN for plain text authentication and AUTHENTICATE for more secure authentication mechanisms. Currently Mail::IMAPClient supports DIGEST-MD5, CRAM-MD5, LOGIN, PLAIN (SASL), and NTLM authentication. There are also a number of methods and parameters that you can use to build your own authentication mechanism. Since this topic is a source of many questions, I will provide a quick overview here. All of the methods and parameters discussed here are described in more detail elsewhere in this document; this section is meant to help you get started. First of all, if you just want to do plain text authentication and your server is okay with that idea then you don't even need to read this section. Second of all, the intent of this section is to help you implement the authentication mechanism of your choice, but you will have to understand how that mechanism works. There are I of authentication mechanisms and most of them are not available to me to test with for one reason or another. Even if this section does not answer all of your authentication questions it I contain all the answers that I have, which I admit are scant. Third of all, if you manage to get any advanced authentication mechanisms to work then please consider donating them to this module. I don't quite have a framework visualized for how different authentication mechanisms could "plug in" to this module but I would like to eventually see this module distributed with a number of helper modules to implement various authentication schemes. The B's support for add-on authentication mechanisms is pretty straight forward and is built upon several assumptions. Basically you create a callback to be used to provide the response to the server's challenge. The I parameter contains a reference to the callback, which can be an anonymous subroutine or a named subroutine. Then, you identify your authentication mechanism, either via the I parameter or as an argument to L. You may also need to provide a subroutine to encrypt (or whatever) data before it is sent to the server. The I parameter must contain a reference to this subroutine. And, you will need to decrypt data from the server; a reference to the subroutine that does this must be stored in the I parameter. This framework is based on the assumptions that a) the mechanism you are using requires a challenge-response exchange, and b) the mechanism does not fundamentally alter the exchange between client and server but merely wraps the exchange in a layer of encryption. It particularly assumes that the line-oriented nature of the IMAP conversation is preserved; authentication mechanisms that break up messages into blocks of a predetermined size may still be possible but will certainly be more difficult to implement. Alternatively, if you have access to B, a utility included in the Cyrus IMAP distribution, you can use that utility to broker your communications with the IMAP server. This is quite easy to implement. An example, L, can be found in the C subdirectory of the source distribution. The following list summarizes the methods and parameters that you may find useful in implementing advanced autentication: =over 4 =item authenticate method This method implements the AUTHENTICATE IMAP client command as documented in RFC2060. If you have set the I parameter then the L method will call L instead of doing a clear text login, which is its normal behavior. If you don't want B to call B on your behalf then you can call it yourself. Instead of setting an I you can just pass the authmechanism as the first argument to AUTHENTICATE. =item Socket and RawSocket Parameters Both parameters hold a reference to the socket connection. Normally this is set for you by the L method, but if you are implementing an advanced authentication technique you may choose to set up your own socket connection and then set this parameter manually, bypassing the B method completely. This is also useful if you want to use L alternatives, like L. The I parameter simply records the socket to use for future operations, without attempting any interaction on it. In this case, you have to be sure to handle all the preliminar operations and to manually set the B object in sync with its actual status with respect to this socket (see below for additional parameters regarding this, especially the I parameter). The I parameter, instead, also attempts to carry on preliminar phases if the conditions apply. If both parameters are present, this takes the precedence over I. It is primarily used to provide an alternative socket for communications, e.g. to use L instead of L used by L by default. B As of version 2.99_04 of this module, the I parameter has changed semantics to make it more "DWIM". The I parameter was introduced as a replacement for the I parameter in older version. =item State, Server, Proxy, Password, and User Parameters If you need to make your own connection to the server and perform your authentication manually, then you can set these parameters to keep your B object in sync with its actual status. Of these, only the I parameter is always necessary. The others need to be set only if you think your program will need them later. I is required for PLAIN (SASL) authentication. =item Authmechanism Set this to the value that AUTHENTICATE should send to the server as the authentication mechanism. If you are brokering your own authentication then this parameter may be less useful. It is also not needed by the L method. It exists solely so that you can set it when you call L to instantiate your object. The B method will call L, who will call L. If B sees that you've set an I then it will call B, using your I and I parameters as arguments. =item Authuser Normally you authenticate and log in with the username specified in the User parameter. When you are using DIGEST-MD5 as I, you can optionally specify a different username for the final log in. This can be useful to mark messages as seen for the I if you don't know the password of the user as the seen state is often a per-user state. =item Authcallback The I parameter, if set, should contain a pointer to a subroutine. The L method will use this as the callback argument to the B method if the I and I parameters are both set. If you set I but not I then the default callback for your mechanism will be used. All supported authentication mechanisms have a default callback; in every other case not supplying the callback results in an error. Most advanced authentication mechanisms require a challenge-response exchange. After the L method sends " AUTHENTICATE \r\n" to the IMAP server, the server replies with a challenge. The B method then invokes the code whose reference is stored in the I parameter as follows: $Authcallback->($challenge,$imap) where C<$Authcallback> is the code reference stored in the I parameter, C<$challenge> is the challenge received from the IMAP server, and C<$imap> is a pointer to the B object. The return value from the I routine should be the response to the challenge, and that return value will be sent by the L method to the server. =item Readmethod The I parameter points to a routine that will read data from the socket connection. This read method will replace the B that would otherwise be performed by B. The replacement method is called with five arguments. The first is a pointer to the B object; the rest are the four arguments required by the B function. Note the third argument (which corresponds to the second argument to B) is a buffer to read into; this will be a pointer to a scalar. So for example if your I were just going to replace B without any intervening processing (which would be silly but this is just an example after all) then you would set your I like this: $imap->Readmethod( sub { my($self) = shift; my($handle,$buffer,$count,$offset) = @_; return sysread( $handle, $$buffer, $count, $offset); } ); Note particularly the double dollar signs in C<$$buffer> in the B call; this is not a typo! =item Prewritemethod The I, if defined, should contain a pointer to a subroutine. It is called immediately prior to writing to the socket connection. It is called by B with two arguments: a reference to the B object and the ASCII text string to be written. It should return another string that will be the actual string sent to the IMAP server. The idea here is that your I will do whatever encryption is necessary and then return the result to the caller so it in turn can be sent to the server. =item Ignoresizeerrors Certain (caching) servers, like Exchange 2007, often report the wrong message size. Instead of chopping the message into a size that it fits the specified size, the reported size will be simply ignored when this parameter is set to C<1>. =item Supportedflags Especially when C is used, the receiving peer may need to be configured explicitly with the list of supported flags; that may be different from the source IMAP server. The names are to be specified as an ARRAY. Black-slashes and casing will be ignored. You may also specify a CODE reference, which will be called for each of the flags seperately. In this case, the flags are not (yet) normalized. The returned lists of the CODE calls are shape the resulting flag list. =back =head2 Errors If you attempt an operation that results in an error, then you can retrieve the text of the error message by using the L method. However, since the L method is an object method (and not a class method) you will only be able to use this method if you've successfully created your object. Errors in the L method can prevent your object from ever being created. Additionally, if you supply the I, I, and I parameters to L, it will attempt to call B and B, either of which could fail and cause your L method call to return C (in which case your object will have been created but its reference will have been discarded before ever having been returned to you). If this happens to you, you can always check C<$@>. B will populate that variable with something useful if either of the L, L, or L methods fail. In fact, as of version 2, the C<$@> variable will always contain error info from the last error, so you can print that instead of calling L if you wish. If you run your script with warnings turned on (which I'm sure you'll do at some point because it's such a good idea) then any error message that gets placed into the L slot (and/or in C<$@>) will automatically generate a warning. =head2 Transactions RFC2060 requires that each line in an IMAP conversation be prefixed with a tag. A typical conversation consists of the client issuing a tag-prefixed command string, and the server replying with one of more lines of output. Those lines of output will include a command completion status code prefixed by the same tag as the original command string. The B module uses a simple counter to ensure that each client command is issued with a unique tag value. This tag value is referred to by the B module as the transaction number. A history is maintained by the B object documenting each transaction. The L method returns the number of the last transaction, and can be used to retrieve lines of text from the object's history. The L parameter is used to control the size of the session history so that long-running sessions do not eat up unreasonable amounts of memory. See the discussion of L under L<"Parameters"> for more information. The L transaction returns the history of the entire IMAP session since the initial connection or for the last I transactions. This provides a record of the entire conversation, including client command strings and server responses, and is a wonderful debugging tool as well as a useful source of raw data for custom parsing. =head1 CLASS METHODS There are a couple of methods that can be invoked as class methods. Generally they can be invoked as an object method as well, as a convenience to the programmer. (That is, as a convenience to the programmer who wrote this module, as well as the programmers using it. It's easier I to enforce a class method's classiness.) Note that if the L method is called as an object method, the object returned is identical to what have would been returned if L had been called as a class method. It doesn't give you a copy of the original object or anything like that. =head2 new Example: Mail::IMAPClient->new(%args) or die "Could not new: $@\n"; The L method creates a new instance of an B object. If the I parameter is passed as an argument to B, then B will implicitly call the L method, placing the new object in the I state. If I and I values are also provided, then L will in turn call L, and the resulting object will be returned from B in the I state. If the I parameter is not supplied then the B object is created in the I state. If the B method is passed arguments then those arguments will be treated as a list of key=>value pairs. The key should be one of the parameters as documented under L<"Parameters">, below. Here are some examples: use Mail::IMAPClient; # returns an unconnected Mail::IMAPClient object: my $imap = Mail::IMAPClient->new; # ... # intervening code using the 1st object, then: # (returns a new, authenticated Mail::IMAPClient object) $imap = Mail::IMAPClient->new( Server => $host, User => $id, Password=> $pass, Clear => 5, # Unnecessary since '5' is the default # ... # Other key=>value pairs go here ) or die "Cannot connect to $host as $id: $@"; See also L<"Parameters">, below, and L<"connect"> and L<"login"> for information on how to manually connect and login after B. =head2 Authenticated Example: $Authenticated = $imap->Authenticated(); # or: $imap->Authenticated($new_value); # But you'll probably never need to do this returns a value equal to the numerical value associated with an object in the B state. This value is normally maintained by the B module, so you typically will only query it and won't need to set it. B For a more programmer-friendly idiom, see the L, L, L, and L object methods. You will usually want to use those methods instead of one of the above. =head2 Connected Example: $Connected = $imap->Connected(); # or: $imap->Connected($new_value); # But you'll probably never need to do this returns a value equal to the numerical value associated with an object in the B state. This value is normally maintained by the B module, so you typically will only query it and won't need to set it. B For a more programmer-friendly idiom, see the L, L, L, and L object methods. You will usually want to use those methods instead of one of the above. =head2 Quote Example: $imap->search(HEADER => 'Message-id' => $imap->Quote($msg_id)); The B method accepts a value as an argument. It returns its argument as a correctly quoted string or a literal string. Note that you should not use this on folder names, since methods that accept folder names as an argument will quote the folder name arguments appropriately for you. (Exceptions to this rule are methods that come with IMAP extensions that are not explicitly supported by B.) If you are getting unexpected results when running methods with values that have (or might have) embedded spaces, double quotes, braces, or parentheses, then you may wish to call B to quote these values. You should B use this method with foldernames or with arguments that are wrapped in quotes or parens if those quotes or parens are there because the RFC2060 spec requires them. So, for example, if RFC requires an argument in this format: ( argument ) and your argument is (or might be) "pennies (from heaven)", then you could just use: $argument = "(" . $imap->Quote($argument) . ")" and be done with it. Of course, the fact that sometimes these characters are sometimes required delimiters is precisely the reason you must quote them when they are I delimiting. For example: $imap->Search('SUBJECT',"(no subject)"); # WRONG! Sends this to imap server: # Search SUBJECT (no subject)\r\n $imap->Search('SUBJECT',$imap->Quote("(no subject)")); # Correct! Sends this to imap server: # Search SUBJECT "(no subject)"\r\n On the other hand: $imap->store('+FLAGS',$imap->Quote("(\Deleted)")); # WRONG! Sends this to imap server: # [UID] STORE +FLAGS "(\Deleted)"\r\n $imap->store($imap->Quota('+FLAGS'),"(\Deleted)"); # CORRECT! Sends this to imap server: # [UID] STORE +FLAGS (\Deleted)\r\n In the above, I had to abandon the many methods available to B programmers (such as L and all-lowercase L) for the sake of coming up with an example. However, there are times when unexpected values in certain places will force you to B. An example is RFC822 Message-id's, which I don't contain quotes or parens. So you don't worry about it, until suddenly searches for certain message-id's fail for no apparent reason. (A failed search is not simply a search that returns no hits; it's a search that flat out didn't happen.) This normally happens to me at about 5:00 pm on the one day when I was hoping to leave on time. (By the way, my experience is that any character that can possibly find its way into a Message-Id eventually will, so when dealing with these values take proactive, defensive measures from the very start. In fact, as I was typing the above, a buddy of mine came in to ask advice about a logfile parsing routine he was writing in which the fields were delimited by colons. One of the fields was a Message Id, and, you guessed it, some of the message id's in the log had (unescaped!) colons embedded in them and were screwing up his C. So there you have it, it's not just me. This is everyone's problem.) =head2 Range Example: my %parsed = $imap->parse_headers( $imap->Range($imap->messages), "Date", "Subject" ); The B method will condense a list of message sequence numbers or message UID's into the most compact format supported by RFC2060. It accepts one or more arguments, each of which can be: =over 8 =item a) a message number, =item b) a comma-separated list of message numbers, =item c) a colon-separated range of message numbers (i.e. "$begin:$end") =item d) a combination of messages and message ranges, separated by commas (i.e. 1,3,5:8,10), or =item e) a reference to an array whose elements are like I through I. =back The B method returns a reference to a B object. The object has all kinds of magic properties, one of which being that if you treat it as if it were just a string it will act like it's just a string. This means you can ignore its objectivity and just treat it like a string whose value is your message set expressed in compact format. You may want to use this method if you find that fetch operations on large message sets seem to take a really long time, or if your server rejects these requests with the claim that the input line is too long. You may also want to use this if you need to add or remove messages to your message set and want an easy way to manage this. For more information on the capabilities of the returned object reference, see L. =head2 Rfc2060_date Example: $Rfc2060_date = $imap->Rfc2060_date($seconds); # or: $Rfc2060_date = Mail::IMAPClient->Rfc2060_date($seconds); The B method accepts one input argument, a number of seconds since the epoch date. It returns an RFC2060 compliant date string for that date (as required in date-related arguments to SEARCH, such as "since", "before", etc.). =head2 Rfc2060_datetime Example: $date = $imap->Rfc2060_datetime($seconds); # or: $date = Mail::IMAPClient->Rfc2060_datetime($seconds); The B method accepts one or two arguments: a obligatory timestamp and an optional zone. The zone shall be formatted as C<< [+-]\d{4} >>, and defaults to C<< +0000 >>. The timestamp follows the definition of the output of the platforms specific C