Hello, please sign in or register
You are here: Home

PHP SMTP server for receiving emails

Preface

I want to handle incoming emails and i've drawn a blank with sendmail. I simply want to handle every incoming messages via PHP, for inserting into a database so i can provide a webmail frontend. In sendmail its a bit laborious (i have had to manually attach an alias to every domain, the alias then passes to the scrpt).

Synopsis

This article assumes your savvy with PHP and linux environment, and introduces the inetd program in ubuntu.

Prerequisites: Linux server with PHP-CLI and Inetd

Install inetd

inetd is a internet transfer protocol. It executes a program when a port is accessed remotely.

sudo apt-get install openbsd-inetd

Check its running

ps -ef | grep inetd

Add the line to the file

#...
# protocol  
smtp    stream  tcp     nowait  root    /php/cli/emailhandler.php

#...

 And the PHP script /php/cli/emailhandler.php

NOTE: chmod+x the file and remove any weird carriage returns if editing from a windows IDE otherwise you might see a message with "^M" e.g. "/usr/bin/php^M: bad interpreter"

#! /usr/bin/php5
<?php
/**
 * This enables a two way conversation between client and local server.
 * The comunication is based upon the SMPT standards defined in http://www.lesnikowski.com/mail/Rfc/rfc2821.txt
 * 
 * 220 Server ready: a connection to the server has been made and the server is ready
 * 221 Server closing: the server accepted the QUIT command and is ready to close the connection
 * 250 OK: the server accepted the command (one of several similar responses)
 * 354 Send mail: the server accepted the DATA command and is waiting for the Mail Message
 * 550 Invalid: the server rejected the command (one of several similar responses) 
 */


function store( $s )
{
	file_put_contents( "/tmp/emails.log", "\n" . $s, FILE_APPEND );
}

function reply( $s )
{
	store( "REPLY:$s" );
	fwrite(STDOUT, $s . "\n" );
}

store( "START: " . date('c') );
reply( "220 Server ready for transmission");

/**
 * The return should be "HELO something"
 * Lets loop through all the requests and respond to them with "250 Ok"
 */
$s = "";

while( ( $data = fgets(STDIN) )
		&& stripos( $data, "QUIT") === false )
{
	$s .= $data;

	store( $data );
	
	if ( ! ( $data_section = ( ( @$data_section && (strpos( $data , "." ) !== 0 ) ) ) ) )
	{
		reply("250 Ok");
		$data_section = ( strpos( $data, "DATA") === 0 );
	}
}

store( $data );

/**
 * Send the term signal "221 Closing transmission"
 */
reply("221 closing transmission");

store( $s . "\nCOMPLETED" );

?>

 And thats it. This will populate a file called /tmp/emails.log which contains the content of the transmission.

Mock Up Using Netcat

I'm replicating the sending of a message using netcat. In the transcript that follows i'm using netcat as an SMTP sender on "sender.local" for transmission to the above PHP code and inetd which together make up the SMTP receiver on "receiver.local". Here is the transcript.  Everything in italic bold i've manually typed and the response starts with a number, e.g. 221, 250, 354

sender.local:~$ nc -w 5 receiver.local 25
220 receiver.local Ready for transmission
HELO sender.local
250 Ok MAIL FROM:<mail@mydomain.com>
250 Ok RCPT TO:<mail2@anotherdomain.com>
250 Ok DATA
354 Ready for mail message This is my first mail message
.
250 Ok QUIT
221 closing transmission sender.local:~$

Alternativly i could mimick this using just the one machine on receiver.local e.g.

receiver.local:~$ nc -w 5 localhost 25

Or i could trying connecting to a third party SMTP server... To find a mail servers for gmail.com i use the  host command.

local:~$ host -t mx gmail.com
gmail.com               MX      5 gmail-smtp-in.l.google.com
gmail.com               MX      10 alt1.gmail-smtp-in.l.google.com
...

Selecting one of the addresses i make a connection using "nc" as before... and test my response codes against their's.

local:~$ nc -w 5 alt1.gmail-smtp-in.l.google.com 25
220 mx.google.com ESMTP k21si8982217waf.8
HELO
250 mx.google.com at your service

What's Next...

Whilst the transmission is running we might also like to...

  1. Validate smtp sender commands. i.e. HELO, MAIL FROM, RCPT TO, DATA,QUIT.
  2. Lookup address (does recipient exist)
  3. <opt> Spamfilter (if we really want to send a rejection). Baysian Filter

And once the transmission has completed.

  1. Copy parts of the transmission into a database.
  2. <opt> Let users devide whether its spam and update a Baysian filter

Resources

This took me a while but i found these articles exceptionally useful.

SMTP handshake comunication between client and server.

http://www.cs.stir.ac.uk/~kjt/software/comms/jasper/SMTP.html This was great to see what the transmission is all about.

http://www.faqs.org/rfcs/rfc821.html RFC 821 Simple Mail Transfer Protocol

http://www.greenend.org.uk/rjk/2000/05/21/smtp-replies.html The transmission order of send -> respond made clear

http://www.cs.ucr.edu/~ddreier/cs164as3.pdf I found this schooled assignment laid down a good introduction.

Inetd configuration and monitoring traffic

http://netbsd.org/docs/guide/en/chap-inetd.html Setup for the "Internet Super Server" Inetd

http://www.jfranken.de/homepages/johannes/vortraege/netcat_inhalt.en.html Netcat "TCP/IP swiss army knife" (sudonym "nc") is great for network exploration and allowed me test my inetd settings

Comments

Thanks for Sharing
Nice! I appreciate you sharing what you came up with... It's really nice to see an example of where someone else started to cut down on the 'guess and check' model usually required with doing something non-standard, from scratch via php.
Created 02/04/11
complete waste
its complete waste of time, labour and webspace, when you have all the datas on user\'s mailbox then why the hell you want to store it in database???
Created 31/01/12
many thanks
really, thanks. what i was looking for.
Created 10/04/12
Needed a line feed after the reply to make it work for me
My .net mail client needed a line feed added before it would respond, it also needed a bit of tweaking because an "ok" after the data command is invalid. Otherwise, worked out of the box.
Created 10/08/12
giasoy george
giasoy george ath
Created 03/11/12
Excellent idea
Its an excellent idea to "outsource" the socket handling to the C written technically mature inetd daemon. The only problem is, you have to rewrite the whole SMTP protocol features in PHP. And specially secure TLS features?
Created 24/05/13
Great content
I’ve read all sorts of reviews about Busabout and so they all seem are overwhelmingly optimistic. It seems like you had a total blast, and you also have confident me in which Croatia can be a vacation destination that we must check out.
Created 10/11/13
sadas
dsadasd
Created 18/11/14
kkkkkk
Created 01/04/15
jkjkj
jk
Created 16/11/15
wq123
This hilfiger outlet time swarovski jewelry something really timberland outlet strange, ...
Created 30/06/16
ninestab123
ninest123 One canada goose pas cher gucci outlet thing
Created 21/07/16
Title*
Comment

Prove you are not a robot

To prove you are not a robot, please type in the six character code you see in the picture below
Security confirmation codeI can't see this!
Contact
Name*
Email never shown*
Home Page

Author

Andrew Dodson
Since:Feb 2007

Comment | flag

Categories

Bookmark and Share