5ubliminal@twitter

PHP SMTP Class - Sending EMails In PHP The Easy Way : 5ubliminal's TellinYa

<a href="http://www.tellinya.com/art2/326/">PHP SMTP Class - Sending EMails In PHP The Easy Way : 5ubliminal's TellinYa</a>
5ubliminal's YAMS
You need to know the protocols!

Real coder know the protocols and don't rely on tools and silly functions like the mail function in PHP. The SMTP protocol is easy to understand and use. Just for entertainment I send plain text emails using telnet only by connecting directly to the target MX agent.

A while ago I published a function which would allow you to verify any mailbox you wanted, with pretty good success rate. The function used the getmxrr function in *NIX PHP to get the SMTP relay agent for a mailbox.

How sending an email works:

The SMTP protocol is like real life: anyone can talk and anyone can hear and anyone can ask anyone to deliver a message on his behalf. Same rules apply as in real life: a degree of confidence is required.

1. Sending emails using your own server:

Let's say your address is sample@example.com and you SMTP server is mail.example.com. If you want to send a message method one it to connect to your own server and send the message to itself asking it to relay it for you. So you say to your email server:

U: Hey server! Send this out for me.
S: I'll do it when I have the time.

Later on you will find a notice of failure in your mailbox. If you don't find one then the message might have gone through.

2. Sending emails directly to their server:

Let's say your address is sample@example.com and you SMTP server is mail.example.com. And you want to send an email to dude@sample.com who has the SMTP server: mail.sample.com. You can use DNS MX Records Query (or getmxrr function in PHP) and find out the mail server of dude@sample.com. And then you can connect to it and send the email directly.

U: Hey server! Here's some mail for a mailbox you work for.
S: Oh thanks! Who's it for?
U: dude@sample.com
S: Sorry this mailbox does not exist here or Ok! Got IT!

Failure is known instantly when you send emails directly to their servers.

Sending directly is better:

It's easier to use your own server but sometime direct sending is better. It's better because you get an instant error and you don't need login credentials to send an email to the owner SMTP server. So if the mailbox you send for is hosted on that mail server, that server should not reject you. Spam filters are another story.

When you use relay agents you may need login (AUTH) details for them to send emails. This is done in order to restrict spam and ensure only those who have an account on that server can use it to send emails.

Remember: Step1 will always contain Step2. If you send emails to your own server, your own server will work Step2 out and query the MX agent for the destination address and send it again to it.
I'm pretty sure you didn't understand anything:)

So let's get on with the code. The below code show how you use this and after if you will find the SMTP PHP Class itself!

<?
//---------------------------------------------------------------------------
$smtp=new eSmtp("mail.your-server.com",25);
//-- Set AUTH if required (it usually is unless you send direct email)
$smtp->setAuth("smtp_auth_user","smtp_auth_pass");
$smtp->setFrom("Dude","dude@your-server.com");
//-- To, Cc, BCc will add new headers!
//-- Be predefining values for them we can prevent this script
//-- from writing it's own! So in order to hide the BCc and Cc list
//-- from each other you can just add:
$smtp->addHeader("Bcc","");
$smtp->addHeader("Cc","");
$smtp->isMimeOn=1; //-- Enable MIME
//-- We add some recipients
$smtp->addRecipient("Subscriber","subscriber@subscriber-server.com","to");
$smtp->addRecipient("Subscriber1","subscriber1@subscriber1-server.com","to");
$smtp->addRecipient("Subscriber2","subscriber2@subscriber2-server.com","cc");
$smtp->addRecipient("Subscriber3","subscriber3@subscriber3-server.com","bcc");
//-- Let's attach one file! Or not ...
$smtp->addAttachment($_SERVER['DOCUMENT_ROOT']."/attach.zip","attachment.zip");
//-- Set subject and body
$smtp->setSubject("subject");
$smtp->setBody("body");
//-- Debug set to 1 outputs data
$smtp->isDebug=0;
//-- By setting $connect and $disconnect to true (1)
//-- The function does connect() and disconnect() for us
//-- se we don't do them before and after send
if($smtp->connect()){
    $success = $smtp->send();
    $smtp->disconnect();
}
//---------------------------------------------------------------------------
//-- Do test, learn how it works and ask if you need anything explained
?>

The SMTP PHP Class

The _functions are internal and should NEVER be used! So use them at your own risk.

<?
/*------------------------------------------------------------------------
Copyright: 5ubliminal. Use, alter and distribute freely without
altering the Copyright notice in anyway!
Whenever you refer to this class or publish this / any variation of this
code you will aknowledge copyright and link back to the original
source of this class which is mentioned here:
--
http://www.tellinya.com/read/2008/03/18/326.html
--
Disclaimer: I take no reponsability for anything... blah, blah, blah
------------------------------------------------------------------------*/
class eSmtp{
    //-- SMTP Server to use for delivery!
    var $smtpServer            = "";
    var $smtpPort            = 25;
    //-- Attached files
    var $lstAttachs            = array();
    //-- Recepient list
    var $lstRecipients         = array();
    //-- Headers list
    var $lstHeaders            = array();
    //-- AUTH data for SMTP who need login
    var $authUser            = "";
    var $authPass            = "";
    //-- Reply and From details
    var $replyName            = "";
    var $replyMail            = "";
    var $fromName            = "";
    var $fromMail            = "";
    //-- MIME enabled?
    var $isMimeOn            = 1;
    //-- Debug enabled outputs all sends and receives
    var $isDebug            = 0;
    //-- Enable HTML content in Message Body
    var $isHtml                = 0;
    //-- Message Subject
    var $msgSubject            = "";
    //-- Message Body
    var $msgBody            = "";
    //-- The socket used to do the magic
    var $smtpSocket            = 0;
    //--
    function eSmtp($smtpServer=false,$smtpPort=25){
        //-- Some defines to start with
        define("EOL","\r\n");
        define("MSG_END","\r\n.\r\n");
        define("EOH","\r\n\r\n");
        define("SEP","\r\n--#BOUNDARY#--");
        define("EOM","QUIT\r\n\0");
        //-- We default SMTP server to local host if none given as parameter
        $this->smtpServer    = (($smtpServer!==false) ? $smtpServer : $_SERVER["LOCAL_ADDR"]);
        $this->smtpPort        = $smtpPort;
    }
    //-- Set Message Subject
    function setSubject($msgSubject){
        $this->msgSubject    = $msgSubject;
    }
    //-- Set Message Body
    function setBody($msgBody){
        $this->msgBody        = $msgBody;
    }
    //-- Set From Details
    function setFrom($fromName,$fromMail){
        $this->fromName        = $fromName;
        $this->fromMail        = $fromMail;
    }
    //-- Set Reply-To Details
    function setReplyTo($reply2Name,$reply2Mail){
        $this->replyName    = $reply2Name;
        $this->replyMail    = $reply2Mail;
    }
    //-- Add A New Attachment. You need the path and an internal name
    //-- which can be different from the local file used.
    //-- MIME type will be guessed internally
    function addAttachment($filePath,$fileName){
        $filePath        = str_replace("\\","/",$filePath);
        if(!isset($fileName)){
            $fileName    = substr(strrchr($filePath,'/'),1);
        }
        $fileExtension    = substr(strrchr($fileName,'.'),1);
        $mimeType        = "application/octet-stream";
        if(isset($MimeTypes[$fileExtension]))
            $mimeType=$MimeTypes[$fileExtension];
        return $this->attachFile($filePath,$fileName,$mimeType);
    }
    //-- Same as above but MIME type can be specified
    function attachFile($filePath,$fileName,$mimeType="application/octet-stream"){
        if(!filesize($filePath)) return false;
        $this->lstAttachs[$fileName] = array(
            "Path"    => $filePath,
            "Mime"    => $mimeType
        );
        return true;
    }
    //-- Add recipient: name, mail and type as below
    function addRecipient($recName,$recMail,$recType/*to|cc|bcc*/){
        if(!isset($recType)) $recType="to";
        else $recType=strtolower($recType);
        $recType=strtolower($recType);
        if(!is_array($this->lstRecipients[$recType])){
            $this->lstRecipients[$recType]=array();
        }
        $this->lstRecipients[$recType][$recMail]=$recName;
    }
    //-- Add raw headers (Expert)
    function addHeader($hdrName,$hdrValue){
        $this->lstHeaders[$hdrName]=$hdrValue;
    }
    //-- Set importance, parameter can have below values
    function setImportance($importance/*low|normal|high*/){
        $this->addHeader("X-Importance",$importance);
    }
    //-- Set sensitivity, parameter can have below values
    function setSensitivity($sensitivity/*Personal|Private|Company-Confidential*/){
        $this->addHeader("X-Sensitivity",$sensitivity,"X");
    }
    //-- Set priority, parameter can have below values
    function setPriority($priority/*low|normal|high*/){
        $this->addHeader("X-Priority",$priority);
    }
    //-- Set SMTP server AUTH
    function setAuth($authUser,$authPass){
        $this->authUser    = $authUser;
        $this->authPass    = $authPass;
    }
    //-- Internal function used to output data if debug enabled
    function _debugLine($line,$sent=1){
        $line=trim($line);
        if(!$this->isDebug) return;
        echo nl2br(htmlentities($line)."<br />");
    }
    //-- Internal function used to send data
    function _sendLines($lstLines,$raw=0){
        if($raw){
        }
        if(!is_array($lstLines)){
      &;nbsp;     $lstLines    = str_replace("\r","",$lstLines);
            $lstLines    = explode("\n",$lstLines);
            if(!count($lstLines)) $lstLines=array($lstLines);
        }
        foreach($lstLines as $line){
            $line = trim($line);
            $this->_debugLine($line);
            if(!fputs($this->smtpSocket,$line."\r\n")){
                return false;
            }
        }
        return true;
    }
    //-- Internal function used to receive replies
    function _getAnswer(){
        $line="";
        while(!feof($this->smtpSocket)){
            $ch    = fgetc($this->smtpSocket);
            if(!strlen($ch)) return false;
            if($ch=="\n"){
                $this->_debugLine($line,0<;span class="phpoperator">);
               &;nbsp;if($line[3]==" ") return (int)substr($line,0,3);
                $line    = ""; continue;
            }
            <;span class="phpkeyword">if($ch!="\r") $line.=$ch;
        }
        return false;
    }
    //-- Internal function used to issue AUTH command
    function _authLogin(){
        $buf="AUTH LOGIN";
        $this->_sendLines($buf);
        if($this->_getAnswer()!=334){
            fclose($this->smtpSocket);
            return false;
        }
        $buf=sprintf("%s",base64_encode($this->authUser));
        $this->_sendLines($buf);
        if($this->_getAnswer()!=334){
            fclose($this->smtpSocket);
            return false;
        }
        $buf=sprintf("%s",base64_encode($this->authPass));
        $this->_sendLines($buf);
        if($this->_getAnswer()!=235){
            fclose($this->smtpSocket);
            return false;
        }
        return true;
    }
    //-- Connect to SMTP server
    function connect($timeout=5){
        $errno        = "";
        $errstr        = "";
        $this->smtpSocket =
            fsockopen ($this->smtpServer, $this->smtpPort,
                $errno, $errstr, $timeout);
        if (!$this->smtpSocket){
            $this->_debugString($errno.":".$errstr."\r\n");
            return false;
        }
        if($this->_getAnswer()!=220){
            fclose($this->smtpSocket);
            return false;
        }
        if(($this->authUser!="") && ($this->authPass!=""))
            $buf=sprintf("EHLO %s","localhost");
        else
            $buf=sprintf("HELO %s","localhost");
        $this->_sendLines($buf);
        $hiReply    = $this->_getAnswer();
        if($hiReply == 250){
            return $this->_authLogin();
        }
        $buf=sprintf("HELO %s","localhost");
        if($this->_getAnswer()!=250){
            fclose($this<;/span>->smtpSocket);
            return false;
        }
        return true;
    }
    //-- Disconnect from SMTP server
    function disconnect(){
        $this->_sendLines("QUIT");
        $quitReply = $this->_getAnswer();
        fclose($this->smtpSocket);
        if($quitReply==221)
            return true;
        return true;
    }
    //-- Internal function used to write SMTP recipients
    function _sendRecipients(){
        $result        = 0;
        $mails        = array();
        $mailsErr    = array();
        while(list($type,$list)=each($this->lstRecipients)){
            while(list($mail,$name)=each($list)){
                if(in_array($mail,$mails)) continue;
                $buf    =sprintf("RCPT TO:<%s>",$mail);
                $this->_sendLines($buf);
                $rez    =$this->_getAnswer();
                array_push($mails,$mail);
                if($rez==250) continue;
                array_push($mailsErr,$mail);
                unset($this->lstRecipients[$type][$mail]);
            }
        }
        return ((count($mails)-count($mailsErr))>0);
    }
    //-- Internal functions used to send headers
    function _sendHeaders(){
        reset;($this->lstHeaders);
        while(list($name,$value)=each($this->lstHeaders)){
            $buf    ="$name: $value";
            $this->_sendLines($buf);
        }
        reset($this->lstRecipients);
        while(list($type,$list)=each($this->lstRecipients)){
            $mails    = array();
            while(list($mail,$name)=each($list)){
                array_push($mails,"$name <$mail>");
            }
            $type[0]    =strtoupper($type[0]);
            if(isset($this->lstHeaders[$type])) continue;
            $buf        ="$type: ".implode(",",$mails)."";
            $this->_sendLines($buf);
        }
        $buf=sprintf("From: %s <%s>",$this->fromName,$this->fromMail);
        $this->_sendLines($buf);
        if(strlen($this->replyMail)){
            $buf=sprintf("Reply-to: %s <%s>",$this->replyName,$this->replyMail);
            $this->_sendLines($buf);
        }
        $buf=sprintf("Subject: %s",$this->msgSubject);
        $this->_sendLines($buf);
        return true;
    }
    //-- Internal function used to send message body depending on encoding: HTML, Text
    function _sendMessage(){
        if($this->isMimeOn){
            $buf =
                "MIME-Version: 1.0\r\n".
                "Content-type: multipart/mixed; boundary=\"#BOUNDARY#\"\r\n\r\n";
            $this->_sendLines($buf);
            $buf=
                "\r\n--#BOUNDARY#\r\n".
                "Content-Type: text/".($this->isHtml ? "html" : "plain")."; charset=us-ascii\r\n";
            $this->_sendLines($buf);
        }else{
            $buf="\r\n";
            $this->_sendLines($buf);
        }
        $this->_sendLines($this->msgBody,1);
        return true;
    }
    //-- Internal function used to send attachments
    function _sendAttachments(){
        if(!$this->isMimeOn) return true;
        if(!count($this->lstAttachs)) return true;
        while(list($name,$file)=each($this->lstAttachs)){
            $fpath        = $file['Path'];
            $mime        = $file['Mime'];
            $fname        = $this->names[$i];
            $newfile    = fopen($fpath,"rb");
            $content    = fread($newfile, filesize($fpath));
            fclose($newfile);
            $content    = base64_encode($content);
            $buf =
                sprintf("\r\n\r\n--#BOUNDARY#\r\n".
                "Content-Type: ".$mime.";&;nbsp;name=%s\r\n".
                "Content-Length: ".filesize($file)."\r\n".
                "Content-Transfer-Encoding: base64\r\n".
                "Content-Disposition: attachment; filename=%s\r\n".
                "Content-ID: <%s>\r\n\r\n",
                $name,$name,$name);
            $this->_sendLines($buf);
            $this->_sendLines($content,1);
        }
        return true;
    }
    //-- Send function sends email.
    function send($connect=false,$disconnect=false){
        if($connect)
            if(!$this->connect()) return false;
        //--
        $buf=sprintf("MAIL FROM:<%s>",$this->fromMail);
        $this->_sendLines($buf);
        if($this->_getAnswer()!=250){ fclose($this->smtpSocket); return false; }
        if(!$this->_sendRecipients()){ fclose($this->smtpSocket); return false; }
        //--
        $this->_sendLines("DATA");
        if($this->_getAnswer()!=354){ fclose($this->smtpSocket); return false; }
        if(!$this->_sendHeaders()){ fclose($this->smtpSocket); return false; }
        if(!$this->_sendMessage()){ fclose($this->smtpSocket); return false; }
        if(!$this->_sendAttachments()){ fclose($this->smtpSocket); return false; }
        //--
        $this->_sendLines(MSG_END);
        if($this->_getAnswer()!=250){ fclose($this->smtpSocket); return false; }
        if($disconnect){ $this->disconnect(); }
        return true;
    }
    //--
};
//-- Copyright: 5ubliminal
?>

As you have learned …
… I promptly reply to questions. So when you got them … shoot … in the Comment Form. I might have not explained this as well as I should have but do ask your questions!

22 Comments Posted By Readers :

Add your comment
#1 Garcia from Bolivia web
Posted on Thursday, 20 March, 2008
I get this:
Warning: in_array() [function.in-array]: Wrong datatype for second argument in Z:garciatmailclass.php on line 235
#2 5ubliminal web
Posted on Thursday, 20 March, 2008
I would read how to handle warning from here. Tell me if you get errors using it, warning are just from my crazy coding style.
#3 Garcia from Bolivia web
Posted on Thursday, 20 March, 2008
A minute after I posted my comment I realized how to fix it...

in_array($mail, $mails) - Original
in_array($mails, $mail) - Fixed

It worked OK but I can't get it to send emails to my Live account. Shitty mailserver...
#4 5ubliminal web
Posted on Thursday, 20 March, 2008
Live account may have a short delay. Check back later.
This class is used here to notify you of replies. And works :)

PS: Thanks for the fix. I'll change it on site too.
#5 Garcia from Bolivia web
Posted on Thursday, 20 March, 2008
Can I try sending you an email? If you get it, make sure to comment or email me back =) Thx
#6 5ubliminal web
Posted on Thursday, 20 March, 2008
Sure!
PS: Got a hi email from you stating that I'll live 4ever!
#7 felix from Sweden
Posted on Wednesday, 26 March, 2008
one could suggest that you try the zend framework, it comes with a plugin/class for mailing and supports everything that yours does :)
#8 5ubliminal web
Posted on Wednesday, 26 March, 2008
@felix: I code for fun… and trust my own code. I don't want to learn how to use some1else's code when I can code it myself:)
PS: I'm funny that way.
#9 Datrigga from United States
Posted on Wednesday, 23 April, 2008
I keep running into somewhat of a problem. It is with the QUIT command.
When I debug I get a "500 Syntax error". I noticed that in your code you define EOM but do not use it. Instead you just use "QUIT". I tried swapping it out with EOM but still get that error. The messages send fine, I am just looking for a way to get the error to stop. If I find a solution I will post it.
Thanks
#10 David King from Great Britain web
Posted on Monday, 06 October, 2008
I'm much like you (comment #8) in that I like to write my own code rather than learn someone elses - by-and-large I hate frameworks, but your script is lovely and self-contained! Kudos to ya!

OK, before I try dissecting it too much, how have you found the script working on large mailing lists? I can't imagine connecting and disconnecting to different smtp sockets over and over is too healthy for the old server! What do you think?
#11 5ubliminal web
Posted on Tuesday, 07 October, 2008
Thanks and indeed there is a problem which I fixed in my own version but have not updated it yet.
I'll mail it to you later on as I don't have time to update it on site, not today anyway.

And it's best to send batches. Connect, send a bunch, disconnect and so on.
#12 David King from Great Britain web
Posted on Friday, 10 October, 2008
Ah yes, I've done a little research and (of course) you're right about the batching - I've been guilty of such nasty code as a foreach loop of mail() commands in the past - but it's never been anything serious! The time has come for some proper smtp work like this!

Oh, and there's a *lot* of problems in the code above, not sure if it's coding typos or a problem with your syntax highlighter - or even if it's just this page (I'm new to your site), but there's some really messed up parts of your code output like:

Line 138: &;nbsp; $lstLines = str_replace("
","",$lstLines);
Line 158: $this->_debugLine($line,0);
Line 159: &;nbsp;if($line[3]==" ") return (int)substr($line,0,3);

I tried to catch them all, but I found a lot of odd, inconsistent rogue characters and half-tags...

Anyhow, I ended up using PHPMailer, with some tweaks for speed, but I'd happily use your script if there was a usable version available! ;-)

I'm gonna have a dig around your site now, looks interesting!

Apologies for length
#13 5ubliminal web
Posted on Friday, 10 October, 2008
I sent you my code and how to use it.
Sorry, but totally 4got as I had a lot to do yesterday.

Hope it helps and don't hesitate to ask if there's anything you need explained.
#14 micmania1 from Great Britain web
Posted on Friday, 10 October, 2008
My mail is being sent, but its being recieved as junk mail.

Is there a way around this?
#15 5ubliminal web
Posted on Friday, 10 October, 2008
No. It all depends on the domain that's sending it or the IP address. Even the text.
Try another mail server.
#16 David King from Great Britain web
Posted on Friday, 10 October, 2008
Yeah man, sucks balls to be swamped! Your mail is much appreciated, if there's anything I can do to help you out in return, give me a shout!
#17 Tejas Mehta from India
Posted on Thursday, 23 October, 2008
Hi,

Your work is excellent. i am new to this SMTP concept as our hosting provider stop old method of only mail() function. i found your tutorial very good and easy. i have simply copy down code. did some changes and it works fine. also file size is so small compare to other free scripts and framework.

now can you let me know how to send multiple user email. i know i can run foreach but repleting this line
$smtp->addRecipient("Name","email-ids","to");

all user will get details in their email.
if($smtp->connect())
{
$success = $smtp->send();
$smtp->disconnect();
}

and due to this i can not do it. because of this it will connect and disconnect it 200 times if i set for loop.

do you have any option ? i hope you are understanding my problem. if you have any solution than let me know or email me on my id.

Thank you.
#18 5ubliminal web
Posted on Monday, 27 October, 2008
U've got mail!
#19 Tejas Mehta from India
Posted on Tuesday, 28 October, 2008
Hello 5ubliminal,

I have received your email. Thank you for files. and help. currently we are having vacation here for a week as our new year approaches. i will go through it once i will be back on work. anyway thank you again and Happy New Year from my side.
#20 5ubliminal web
Posted on Tuesday, 28 October, 2008
Wow. You guys are in a hurry :) Have fun and Happy New Year!
#21 Kimmo from Finland
Posted on Wednesday, 05 November, 2008
There's a whole bunch of html-garbage within the code example. Is it possible to get a clean version of the code without the cod coloring. Something might be f'd up with whatever makes the color coding.

Also, I noticed that in my mail server a chunk split was required for sending an attachment file. Otherwise it would reject it because of too long line.
The original was:
$this->_sendLines($content,1);
And my little improvement:
$this->_sendLines(chunk_split($content),1);

And also found a bug:
"Content-Length: ".filesize($file)."
".
should be
"Content-Length: ".filesize($fpath)."
".
#22 5ubliminal web
Posted on Monday, 10 November, 2008
Thanks and true. I have an updated version but I just didn't have time to update it here.
Will let u now when's done.
5ubliminal's TellinYa.com SEM & SEO Blog © 2007 - All rights reserved unless mentioned otherwise .
Rendered On : [Friday, 12 March, 2010 - 15:13:48 GMT]   No Ajax / Flash Used Here
" PHP SMTP Class - Sending EMails In PHP The Easy Way : 5ubliminal's TellinYa "
Close
Tellinya.com is relocating to blog.5ubliminal.com. This blog is no longer maintained and comments are no longer accepted / answered.