OVERVIEW
Do you want to send today's entry to your blog? Do you have a
newsletter that cannot detect false receiver email addresses? Even
though mailing lists are often supported by software such as mailman, ezmlm or
majordomo it can be of great value to be able to receive mails via php.
Newsletter, trouble-ticket-systems, an archive of mails, or an email
based administration of websites or onlineshops are being enabled, if received
mails are automatically detected upon arrival.
You can achieve this by regularly connecting your POP or IMAP Server.
Problems occur when these servers are only allowing so many connections
per hour, file attachments are having to be processed or wrong passwords and
other causes of error have to be taken care of. One of the main problems
is you are not being notified once an email arrives. You have to connect to
your account and download the mail. The download of big mails will slow
down your script and due to this circumstance you might receive emails that
are a minute or an hour old. On the other hand, you might connect to your
account a thousand times without downloading/receiving any mail at all.
My solution (Bjoern SChotte deals with this subject on
contentmanager.de) is based on a PHP scriptfile that is being called up once mail is being
received. Requirement: A user account on the mailserver. The
.forward-file in the directory of this user might be interpreted by postfix or
sendmail, and will then start the script.
If you are working with an application that does not have to be
informed in case of an arriving email, then POP3 and the IMAP functions are great to
work with, as well as the PEAR-POP-functions or other implementations.
HOW IT WORKS
A PHP-Script is being called from the mailserver and receives the
incoming mail as standard-input. This mail is being forwarded to a URL via
HTTP-POST, such as a regular entry to a web formular. Also, because of the
.forward file you can forward the mail to other users automatically or you
can store it in a folder of your choice.
In order to start a PHP-Script from Postfix or sendmail, it has to be
processed just as a Shellscript. It should contain the directory of your
php-interpreter, such as:
#!/usr/local/bin/php
Even though many are just using PHP as an Apache-module, the interpreter
usually is installed in a self running file on the computer. On most
computers you can find it in /usr/local/bin/, on others you can find it
under /usr/bin/.
If you are labelling the created file as self-running (chmod a+x
scriptfilename) and then start it, your php-sourcecode in the shell -
just as a regular shellscript - should run.
OPEN THE ENVELOPE
Now we have to read out the standard input. As said above, Bjoern
already wrote about this, and it is just as easy as opening a file.
To be precise, a file such as "php://stdIn". This pseudo-URL allows
you to read out the standard input as follows:
<?php
$res="";
if ($fp=fopen("php://stdIn", "r"))
{
while (!feof($fp))
{
$line=fgets($fp, 4096);
$res.=$line;
}
fclose($fp);
}
echo "\n standard input was $res \n";
?>
You could now parse the subject and the header-info, but the author
likes to keep it general. My script collects the standard input and posts it to
a URL such as it would be entered in a regular web form. In order to have it
work for every application, we are not parsing. You could enhance the script
so that every entry to the email header is being posted, such as it was an
imaginary hidden field in a web formular. Doing this, you would make
everything more complicated and you would specialize your application to
email only.
POSTING OF THE CONTENT
There is a problem to run an HTTP-POST with PHP. PHP does not support
that in the implemented scope of languate. The script from "nf@bigpond.net.au"
(on nf.wh3rd.net/projects/http.inc/) emulates exactly this function, however.
It looks something like this:
<?php
$headers = "POST <URL> HTTP/1.1\r\nConnection:
Keep-Alive\r\nUser-Agent: <some faked user agent>\r\nPragma:
no-cache\r\nCache-control: no-cache\r\nAccept: */*\r\nAccept-Language:
de,en,en-au\r\nHost: <server>\r\nContent-Type:
application/x-www-form-urlencoded\r\nContent-Length: <length of
urlencoded
length>\r\n\r\n";
$fp = fsockopen($server, $port, $errno, $errstr);
if (!$fp)
{
return false;
}
fputs($fp, $headers);
fputs($fp, $urlencoded);
$res="";
while (!feof($fp)) $res.=fgets($fp, 1024);
// what the Server shows is now in $res
fclose($fp);
?>
Unfortunately the script was a little slow for our use: On a
Loop-Back-Ethernet of a somewhat unused Apache 2 with 512 MB Ram and 2.5
Ghz, a process took approximately 15 seconds. Appearantly it wasn't the
submitting that was slow, but rather it was waiting for the response for
that long. However, if you are ignoring the answer of the HTTP server the
script ran in less than 0.01 seconds:
<?php
if (!$discard_return)
{
while (!feof($fp)) $res.= fgets($fp, 1024);
}
?>
SETUP OF THE RECEIVER ACCOUNT
The script can be used in .forward files, simply write:
|"postinput.php http://yourdomain.com/yourscript.php"
in the .forward-Datei in the home-directory of the user, which mail you
wish to receive. Your email will literally post this: (to
http://yourdomain.com/yourscript.php).
Your .forward does not have to stop here however:
|"postinput.php http://yourdomain.com/yourscript.php"
|"postinput.php http://mydomain.com/yourscript.php"
\jstaerk
scipio.africanus@rome.spqr
sends an incoming mail to yourdomain and mydomain, puts a copy in the
mailbox of the user jstaerk and forwards one copy to Scipio Africanus.
THE RECEIVER SCRIPT
In the script yourdomain.com/yourscript.php you can now read out
$_POST["stdIn"]. The echo-allocation in this script will be ignored,
because the user did not submit the imaginary (hidden) field via a web browser,
but with his standard mail client.
<?php
$f=fopen("/tmp/output.txt", "w");
fputs($f, "I got the following mail:".$_POST["stdIn"]);
echo "Received mail:".$_POST["stdIn"];
fclose ($f);
?>
FINDINGS
The script is fairly versatile because it is not limited to posting
mails, but also further standard inputs. Also, other scripts such as JSP files and
Perl Scripts would be imaginable to receive the standard input. Maybe
you want to use the output of a cron-job in PHP? Should you have interesting
ideas, let me know or simply comment this article.
-Jochen