56
The underlying library PHP uses to support IMAP and POP3 offers a seemingly unending
number of features that allow you to essentially write an entire mail client. With all those
features, however, comes complexity. In fact, there are currently 63 different functions in PHP
beginning with the word
imap
, and that doesn't take into account that some also speak POP3
and NNTP.
However, the basics of talking with a mail server are straightforward. Like many features in
PHP, you begin by opening the connection and grabbing a handle:
$mail = imap_open('{mail.server.com:143}', 'username', 'password');
This opens an IMAP connection to the server named mail.server.com on port 143. It also
passes along a username and password as the second and third arguments.
To open a POP3 connection instead, append
/pop3
to the end of the server and port. Since
POP3 usually runs on port 110, add
:110
after the server name:
$mail = imap_open('{mail.server.com:110/pop3}', 'username', 'password');
To encrypt your connection with SSL, add
/ssl
on to the end, just as you did with
pop3
. You
also need to make sure your PHP installation is built with the
--with-imap-ssl
configuration
option in addition to
--with-imap
. Also, you need to build the system IMAP library itself with
SSL support. If you're using a self-signed certificate and wish to prevent an attempted
validation, also add
/novalidate-cert
. Finally, most SSL connections talk on either port
993 or 995. All these options can come in any order, so the following is perfectly legal:
$mail = imap_open('{mail.server.com:993/novalidate-cert/pop3/ssl}',
'username', 'password');
Surrounding a variable with curly braces inside of a double-quoted string, such as
{$var}
, is
a way to tell PHP exactly which variable to interpolate. Therefore, to use interpolated variables
in this first parameter to
imap_open( )
, escape the opening
{
:
$server = 'mail.server.com';
$port = 993;
$mail = imap_open("\{$server:$port}", 'username', 'password');
Once you've opened a connection, you can ask the mail server a variety of questions. To get a
listing of all the messages in your inbox, use
imap_headers( )
:
$headers = imap_headers($mail);
This returns an array in which each element is a formatted string corresponding to a message:
A 189) 5-Aug-2002 Beth Hondl an invitation (1992 chars)
76
Alternatively, to retrieve a specific message, use
imap_header( )
and
imap_body( )
to
pull the header object and body string:
$header = imap_header($message_number);
$body = imap_body($message_number);
The
imap_header( )
function returns an object with many fields. Useful ones include
subject
,
fromaddress
, and
udate
. All the fields are listed in Table 17-2
in Section 17.6
.
The
body
element is just a string, but, if the message is a multipart message, such as one
that contains both a HTML and a plain-text version,
$body
holds both parts and the MIME
lines describing them:
------=_Part_1046_3914492.1008372096119
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Plain-Text Message
------=_Part_1046_3914492.1008372096119
Content-Type: text/html
Content-Transfer-Encoding: 7bit
<html>HTML Message</html>
------=_Part_1046_3914492.1008372096119--
To avoid this occurrence, use
imap_fetchstructure( )
in combination with
imap_fetchbody( )
to discover how the body is formatted and to extract just the parts you
want:
// pull the plain text for message $n
$st = imap_fetchstructure($mail, $n);
if (!empty($st->parts)) {
for ($i = 0, $j = count($st->parts); $i < $j; $i++) {
$part = $st->parts[$i];
if ($part->subtype == 'PLAIN') {
$body = imap_fetchbody($mail, $n, $i+1);
}
}
} else {
$body = imap_body($mail, $n));
}
If a message has multiple parts,
$st->parts
holds an array of objects describing them. The
part
property holds an integer describing the main body MIME type. Table 17-1
lists which
numbers go with which MIME types. The
subtype
property holds the MIME subtype and tells
if the part is
plain
,
html
,
png
, or another type, such as
octet-stream
.
Table 17-1. IMAP MIME type values
Number MIME type
PHP constant
Description
Examples
79
0
text
TYPETEXT
Unformatted text
Plain text, HTML, XML
1
multipart
TYPEMULTIPART
Multipart message
Mixed, form data, signed
2
message
TYPEMESSAGE
Encapsulated message News, HTTP
3
application
TYPEAPPLICATION
Application data
Octet stream, PDF, Zip
4
audio
TYPEAUDIO
Music file
MP3, RealAudio
5
image
TYPEIMAGE
Graphic image
GIF, JPEG, PNG
6
video
TYPEVIDEO
Video clip
MPEG, Quicktime
7
other
TYPEOTHER
Everything else
VRML models
17.4.4 See Also
Section 17.2
and Section 17.4
for more on sending mail; documentation on
imap_open( )
at
http://www.php.net/imap_open
,
imap_header( )
at http://www.php.net/imap-header
,
imap-body( )
at http://www.php.net/imap-body
, and IMAP in general at
http://www.php.net/imap
.
Recipe 17.5 Posting Messages to Usenet Newsgroups
17.5.1 Problem
You want to post a message to a Usenet newsgroup, such as comp.lang.php.
17.5.2 Solution
Use
imap_mail_compose( )
to format the message, then write the message to the server
using sockets:
$headers['from'] = 'adam@example.com';
$headers['subject'] = 'New Version of PHP Released!';
$headers['custom_headers'][] = 'Newsgroups: comp.lang.php';
$body[0]['type'] = TYPETEXT;
$body[0]['subtype'] = 'plain';
$body[0]['contents.data'] = 'Go to http://www.php.net and download it
today!';
$post = imap_mail_compose($headers, $body);
$server = 'nntp.example.com';
$port = 119;
$sh = fsockopen($server, $port) or die ("Can't connect to $server.");
fputs($sh, "POST\r\n");
fputs($sh, $post);
fputs($sh, ".\r\n");
fclose($sh);
17.5.3 Discussion
78
No built-in PHP functions can post a message to a newsgroup. Therefore, you must open a
direct socket connection to the news server and send the commands to post the message.
However, you can use
imap_mail_compose( )
to format a post and create the headers and
body for the message. Every message must have three headers: the
From
: address, the
message
Subject
:, and the name of the newsgroup:
$headers['from'] = 'adam@example.com';
$headers['subject'] = 'New Version of PHP Released!';
$headers['custom_headers'][ ] = 'Newsgroups: comp.lang.php';
Create an array,
$headers
, to hold the message headers. You can directly assign the values
for the
From
: and
Subject
: headers, but you can't do so for the
Newsgroups
: header.
Because
imap_mail_compose( )
is most frequently used to create email messages, the
Newsgroups
: header is not a predefined header. To work around this, you must instead add it
with the
custom_headers
array element.
There is a different syntax for the
custom_headers
. Instead of placing the lowercase header
name as the element name and the header value as the array value, place the entire header
as an array value. Between the header name and value, add a colon followed by a space. Be
sure to correctly spell
Newsgroups
: with a capital
N
and final
s
.
The message body can contain multiple parts. As a result, the body parameter passed to
imap_mail_compose( )
is an array of arrays. In the Solution, there was only one part, so
we directly assign values to
$body[0]
:
$body[0]['type'] = TYPETEXT;
$body[0]['subtype'] = 'plain';
$body[0]['contents.data'] = 'Go to http://www.php.net and download it
today!';
Each message part needs a MIME type and subtype. This message is ASCII, so the type is
TYPETEXT
, and the subtype is
plain
. Refer back to Table 17-1
in Section 17.4
for a listing of
IMAP MIME type constants and what they represent. The
contents.data
field holds the
message body.
To convert these arrays into a formatted string call
imap_mail_compose($body,
$headers)
. It returns a post that looks like this:
From: adam@example.com
Subject: New Version of PHP Released!
MIME-Version: 1.0
Content-Type: TEXT/plain; CHARSET=US-ASCII
Newsgroups: comp.lang.php
Go to http://www.php.net and download it today!
Armed with a post the news server will accept, call
fsockopen( )
to open a connection:
56
$server = 'nntp.example.com';
$port = 119;
$sh = fsockopen($server, $port) or die ("Can't connect to $server.");
The first parameter to
fsockopen( )
is the hostname of the server, and the second is the
port to use. If you don't know the name of your news server, try the hostnames news, nntp,
or news-server in your domain: for example, news.example.com, nntp.example.com, or news-
server.example.com. If none of these work, ask your system administrator. Traditionally, all
news servers use port
119
.
Once connected, you send the message:
fputs($sh, "POST\r\n");
fputs($sh, imap_mail_compose($headers, $body));
fputs($sh, ".\r\n");
The first line tells the news server that you want to post a message. The second is the
message itself. To signal the end of the message, place a period on a line by itself. Every line
must have both a carriage return and a newline at the end. Close the connection by calling
fclose($sh)
.
Every message on the server is given a unique name, known as a
Message-ID
. If you want
to reply to a message, take the
Message-ID
of the original message and use it as the value
for a
References
header:
// retrieved when reading original message
$message_id = '<20030410020818.33915.php@news.example.com>';
$headers['custom_headers'][] = "References: $message_id";
17.5.4 See Also
Section 17.6
for more on reading newsgroups; documentation on
imap_mail_compose( )
at http://www.php.net/imap-mail-compose
,
fsockopen( )
at
http://www.php.net/fsockopen
,
fputs( )
at http://www.php.net/fputs
, and
fclose( )
at
http://www.php.net/fclose
; RFC 977 at http://www.faqs.org/rfcs/rfc977.html
.
Recipe 17.6 Reading Usenet News Messages
17.6.1 Problem
You want to read Usenet news messages using NNTP to talk to a news server.
17.6.2 Solution
Use PHP's IMAP extension. It also speaks NNTP:
50
// open a connection to the nntp server
$server = '{news.php.net/nntp:119}';
$group = 'php.general'; // main PHP mailing list
$nntp = imap_open("$server$group", '', '', OP_ANONYMOUS);
// get header
$header = imap_header($nntp, $msg);
// pull out fields
$subj = $header->subject;
$from = $header->from;
$email = $from[0]->mailbox."@".$from[0]->host;
$name = $from[0]->personal;
$date = date('m/d/Y h:i A', $header->udate);
// get body
$body = nl2br(htmlspecialchars(imap_fetchbody($nntp,$msg,1)));
// close connection
imap_close($nntp);
17.6.3 Discussion
Reading news from a news server requires you to connect to the server and specify a group
you're interested in reading:
// open a connection to the nntp server
$server = "{news.php.net/nntp:119}";
$group = "php.general";
$nntp = imap_open("$server$group",'','',OP_ANONYMOUS);
The function
imap_open( )
takes four parameters. The first specifies the news server to use
and the newsgroup to read. The server here is news.php.net, the news server that mirrors all
the PHP mailing lists. Add
/nntp
to let the IMAP extension know you're reading news instead
of mail, and specify
119
as a port; that's typically the port reserved for NNTP. NNTP stands for
Network News Transport Protocol; it's used to communicate with news servers, just as HTTP
communicates with web servers. The group is php.general, the main mailing list of the PHP
community.
The middle two arguments to
imap_open( )
are a username and password, in case you need
to provide verification of your identity. Because news.php.net is open to all readers, leave
them blank. Finally, pass the flag
OP_ANONYMOUS
, which tells IMAP you're an anonymous
reader; it will not then keep a record of you in a special .newsrc file.
Once you're connected, you usually want to either get a general listing of recent messages or
all the details about one specific message. Here's some code that displays recent messages:
// read and display posting index
$last = imap_num_msg($nntp);
$n = 10; // display last 10 messages
// table header
67
print <<<EOH
<table>
<tr>
<th align="left">Subject</th>
<th align="left">Sender</th>
<th align="left">Date</th>
</tr>
EOH;
// the messages
for ($i = $last-$n+1; $i <= $last; $i++) {
$header = imap_header($nntp, $i);
if (! $header->Size) { continue; }
$subj = $header->subject;
$from = $header->from;
$email = $from[0]->mailbox."@".$from[0]->host;
$name = $from[0]->personal ? $from[0]->personal : $email;
$date = date('m/d/Y h:i A', $header->udate);
print <<<EOM
<tr>
<td><a href="$_SERVER[PHP_SELF]"?msg=$i\">$subj</a></td>
<td><a href="mailto:$email">$name</a></td>
<td>$date</td>
</tr>
EOM;
}
// table footer
echo "</table>\n";
To browse a listing of posts, you need to specify what you want by number. The first post ever
to a group gets number 1, and the most recent post is the number returned from
imap_num_msg( )
. So, to get the last
$n
messages, loop from
$last-$n+1
to
$last
.
Inside the loop, call
imap_header( )
to pull out the header information about a post. The
header contains all the metainformation but not the actual text of the message; that's stored
in the body. Because the header is usually much smaller than the body, this allows you to
quickly retrieve data for many posts without taking too much time.
Now pass
imap_header( )
two parameters: the server connection handle and the message
number. It returns an object with many properties, which are listed in Table 17-2
.
Table 17-2. imap_header( ) fields from a NNTP server
Name
Description
Type
Example
date
or
Date
RFC 822 formatted date:
date('r')
String Fri, 16 Aug 2002 01:52:24 -0400
subject
or
Subject
Message subject
String Re: PHP Cookbook Revisions
message_id
A unique ID identifying the
String <20030410020818.
97
message
33915.php@news.example.com>
newsgroups
The name of the group the
message was posted to
String php.general
toaddress
The address the message was
sent to
String php-general@lists.php.net
to
Parsed version of
toaddress
field
Object
mailbox: "php-general", host:
"lists-php.net"
fromaddress
The address that sent the
message
String
Ralph Josephs
<ralph@example.net>
from
Parsed version of
fromaddress
field
Object
personal: "Ralph Josephs",
mailbox: "ralph", host:
"example.net"
reply_toaddress
The address you should reply
to, if you're trying to contact
the author
String rjosephs@example.net
reply_to
Parsed version of
reply_toaddress
field
Object
Mailbox: "rjosephs", host:
"example.net"
senderaddress
The person who sent the
message; almost always
identical to the
from
field, but
if the
from
field doesn't
uniquely identify who sent the
message, this field does
String
Ralph Josephs
<ralph@example.net>
sender
Parsed version of
senderaddress
field
Object
Personal: "Ralph Josephs",
mailbox: "ralph", host:
"example.net"
Recent
If the message is recent, or
new since the last time the
user checked for mail
String Y or N
Unseen
If the message is unseen
String Y or " "
Flagged
If the message is marked
String Y or " "
Answered
If a reply has been sent to this
message
String Y or " "
Deleted
If the message is deleted
String Y or " "
Draft
If the message is a draft
String Y or " "
Size
Size of the message in bytes String 1345
udate
Unix timestamp of message
date
Int
1013480645
Mesgno
The number of the message in
the group
String 34943
Documents you may be interested
Documents you may be interested