<?php
/**
*
* SMTP4PHP : SMTP for PHP
* This project is a collection of PHP 5.x classes, written in OOP style,
* providing exception support, dedicated for sending mails fast and easily,
* with or without embedded images and/or attachments.
*
* Copyright (c) 2011, Raul IONESCU <hide@address.com>,
* Bucharest, ROMANIA
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @package SMTP4PHP
* @copyright Copyright (c) 2011, Raul IONESCU.
* @author Raul IONESCU <hide@address.com>
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @access public
*
* PHP versions 5.3 or greater
*/
/*///////////////////////////////////////////////////////////////////////////////////////*/
function exception_error_handler($errno, $errstr, $errfile, $errline )
{ throw new ErrorException($errstr, $errno, 0, $errfile, $errline); }
/*///////////////////////////////////////////////////////////////////////////////////////*/
set_error_handler("exception_error_handler");
/*///////////////////////////////////////////////////////////////////////////////////////*/
/*///////////////////////////////////////////////////////////////////////////////////////*/
/*///////////////////////////////////////////////////////////////////////////////////////*/
class eMailUser implements Iterator
{
const VERSION = eMail::VERSION;
protected $user;
public function __construct($name = NULL, $address = NULL) { $this->user = array('name' => $name, 'address' => $address); }
public function __get($property)
{
switch($property = strtoupper($property))
{
case 'NAME':
case 'ADDRESS':
return $this->user[$property];
default:
return NULL;
}
}
public function __toString() { return (($this->name)?('"'.$this->name.'" '):('')).(($this->address)?('<'.$this->address.'>'):('')); }
public function __invoke($property) { return $this->$property; }
public function current() { return current($this->user); }
public function key() { return key($this->user); }
public function next() { return next($this->user); }
public function rewind() { return reset($this->user); }
public function valid() { return isset($this->user[$this->key()]); }
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
/*///////////////////////////////////////////////////////////////////////////////////////*/
/*///////////////////////////////////////////////////////////////////////////////////////*/
class eMail
{
const VERSION = 2011;
const RELEASE = 1;
const PRIORITY_LOW = 5;
const PRIORITY_NORMAL = 3;
const PRIORITY_HIGH = 1;
const CONTENT_TRANSFER_ENCODING_TEXT = 1;
const CONTENT_TRANSFER_ENCODING_BASE64 = 2;
protected $mixedBoundary;
protected $altBoundary;
protected $returnPath;
protected $from;
protected $replyTo;
protected $to;
protected $cc;
protected $bcc;
protected $priority;
protected $contentTransferEncoding;
protected $subject;
protected $htmlMessage;
protected $textMessage;
protected $images;
protected $attachments;
protected function _generateRandomString() { return md5(uniqid(rand(),true)); }
protected function _generateBoundary() { return $this->_generateRandomString().(($this->from) && (preg_match('/(@.+)$/i',$this->from->address, $m) && is_array($m) && ($m = $m[0]))?($m):($this->_generateRandomString())); }
public function __construct(eMailUser $from = NULL, $to = NULL, $subject = NULL, $htmlMessage = NULL, $textMessage = NULL)
{
$this->From = $from;
$this->To = $to;
$this->priority = self::PRIORITY_NORMAL;
$this->contentTransferEncoding = self::CONTENT_TRANSFER_ENCODING_BASE64;
$this->Subject = $subject;
$this->HTMLMessage = $htmlMessage;
$this->TXTMessage = $textMessage;
$this->images = array();
$this->attachments = array();
$this->mixedBoundary = '--=_'.$this->_generateBoundary();
$this->altBoundary = '--=_'.$this->_generateBoundary();
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
public function __get($property)
{
switch(strtoupper($property))
{
case 'PRIORITY':
return $this->priority;
case 'CONTENTTRANSFERENCODING':
return $this->contentTransferEncoding;
case 'RETURNPATH':
return $this->returnPath;
case 'FROM':
return $this->from;
case 'REPLYTO':
return $this->replyTo;
case 'TO':
return $this->to;
case 'CC':
return $this->cc;
case 'BCC':
return $this->bcc;
case 'SUBJECT':
return $this->subject;
case 'HTMLMESSAGE':
return $this->htmlMessage;
case 'TEXTMESSAGE':
case 'TXTMESSAGE':
return $this->textMessage;
case 'RAWMESSAGE':
return $this->__toString();
default:
return NULL;
}
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
protected function _set(&$property,&$value)
{
try
{
if($value)
{
if($value instanceof eMailUser) { return $property = array($value); }
else if(is_array($value))
{
$values = array();
array_walk_recursive($value, create_function('$value, $key, $obj', 'array_push($obj, $value);'), &$values);
foreach($values as $k=>$v) { if(!$v instanceof eMailUser) { unset($value[$k]); } }
return $property = $values;
}
}
else { if(empty($property)) { return $property = array(); } }
}
catch(Exception $e) { return NULL; }
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
public function __set($property, $value)
{
switch(strtoupper($property))
{
case 'PRIORITY':
return $this->priority = ((($value == self::PRIORITY_HIGH) || ($value == self::PRIORITY_LOW))?($value):(self::PRIORITY_NORMAL));
case 'CONTENTTRANSFERENCODING':
return $this->contentTransferEncoding = (($value == self::CONTENT_TRANSFER_ENCODING_BASE64)?(self::CONTENT_TRANSFER_ENCODING_BASE64):(self::CONTENT_TRANSFER_ENCODING_TEXT));
case 'RETURNPATH':
return ($value && ($value instanceof eMailUser))?($this->returnPath = $value):(NULL);
case 'FROM':
return ($value && ($value instanceof eMailUser))?($this->from = $value):(NULL);
case 'REPLYTO':
return ($value && ($value instanceof eMailUser))?($this->replyTo = $value):(NULL);
case 'TO':
return $this->_set($this->to, $value);
case 'CC':
return $this->_set($this->cc, $value);
case 'BCC':
return $this->_set($this->bcc, $value);
case 'SUBJECT':
return $this->subject = trim(strip_tags($value));
case 'HTMLMESSAGE':
return $this->htmlMessage = trim($value);
case 'TEXTMESSAGE':
case 'TXTMESSAGE':
return $this->textMessage = preg_replace('/[\f\t ]{2,}/mS', ' ', trim(strip_tags($value)));
default:
return NULL;
}
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
public function addImage($file)
{
try
{
$info = getimagesize($file);
$cid = $this->_generateRandomString();
$msg = '--'.$this->mixedBoundary.PHP_EOL;
$msg .= 'Content-Location: "'.basename($file).'"'.PHP_EOL;
$msg .= 'Content-Type: '.image_type_to_mime_type($info[2]).PHP_EOL;
$msg .= 'Content-Transfer-Encoding: base64'.PHP_EOL;
$msg .= 'Content-ID: <'.$cid.'>'.PHP_EOL;
$msg .= 'Content-Disposition: inline; filename="'.basename($file).'"'.PHP_EOL.PHP_EOL;
$msg .= chunk_split(base64_encode(file_get_contents($file))).PHP_EOL;
$this->images[$cid] = $msg;
return 'cid:'.$cid;
}
catch(Exception $e) { return NULL; }
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
public function addAttachment($file)
{
try
{
$cid = $this->_generateRandomString();
$msg = '--'.$this->mixedBoundary.PHP_EOL;
$msg .= 'Content-Type: binary/octet-stream'.PHP_EOL;
$msg .= 'Content-Transfer-Encoding: base64'.PHP_EOL;
$msg .= 'Content-ID: <'.$cid.'>'.PHP_EOL;
$msg .= 'Content-Disposition: attachment; filename="'.basename($file).'"'.PHP_EOL.PHP_EOL;
$msg .= chunk_split(base64_encode(file_get_contents($file))).PHP_EOL;
$this->attachments[$cid] = $msg;
return $cid;
}
catch(Exception $e) { return NULL; }
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
public function __toString()
{
try
{
$msg = '';
if($this->returnPath) { $msg .='Return-Path: <'.$this->returnPath->address.'>'.PHP_EOL; }
if($this->replyTo) { $msg .= 'Reply-To: '.$this->replyTo.PHP_EOL; }
$msg .= 'From: '.$this->from.PHP_EOL;
if(($this->to) && is_array($this->to) && count($this->to)) { $msg .= 'To: '.implode(', ',$this->to).PHP_EOL; }
if(($this->cc) && is_array($this->cc) && count($this->cc)) { $msg .= 'Cc: '.implode(', ',$this->cc).PHP_EOL; }
$msg .= 'Subject: '.$this->subject.PHP_EOL;
$msg .= 'X-Priority: '.$this->priority.PHP_EOL;
$msg .= 'X-MSMail-Priority: '.(($this->priority == self::PRIORITY_HIGH)?('High'):(($this->priority == self::PRIORITY_LOW)?('Low'):('Normal'))).PHP_EOL;
$msg .= 'X-Mailer: PHP/'.phpversion().PHP_EOL;
$msg .= 'MIME-Version: 1.0'.PHP_EOL;
$msg .= 'Content-Type: multipart/mixed; boundary="'.$this->mixedBoundary.'"'.PHP_EOL;
$msg .= 'Date: '.date('r').PHP_EOL;
//$msg .= 'Disposition-Notification-To: <'.$this->from->address.'>'.PHP_EOL;
$msg .= PHP_EOL;
$msg .= '--'.$this->mixedBoundary.PHP_EOL;
$msg .= 'Content-Type: multipart/alternative; boundary="'.$this->altBoundary.'"'.PHP_EOL.PHP_EOL;
$msg .= '--'.$this->altBoundary.PHP_EOL;
/*/////////////////////////////////////////*/
/* text message */
/*/////////////////////////////////////////*/
$msg .= 'Content-Type: text/plain; charset="iso-8859-1"'.PHP_EOL;
if($this->contentTransferEncoding == self::CONTENT_TRANSFER_ENCODING_BASE64)
{
$msg .= 'Content-Transfer-Encoding: base64'.PHP_EOL.PHP_EOL;
$msg .= chunk_split(base64_encode($this->textMessage));
}
else {
$this->textMessage = preg_replace('/^\.$/imsSU','..',$this->textMessage);
$msg .= 'Content-Transfer-Encoding: 8bit'.PHP_EOL.PHP_EOL;
$msg .= $this->textMessage;
}
$msg .= PHP_EOL.PHP_EOL;
$msg .= '--'.$this->altBoundary.PHP_EOL;
/*/////////////////////////////////////////*/
/* html message */
/*/////////////////////////////////////////*/
if(empty($this->htmlMessage))
{ $this->htmlMessage = nl2br($this->TXTMessage); }
$msg .= 'Content-type: text/html; charset="iso-8859-1"'.PHP_EOL;
if($this->contentTransferEncoding == self::CONTENT_TRANSFER_ENCODING_BASE64)
{ $msg .= 'Content-Transfer-Encoding: base64'; }
else { $msg .= 'Content-Transfer-Encoding: 8bit'; }
$msg .= PHP_EOL.PHP_EOL;
if(preg_match('/<\/{0,1}(html|head|body.*)>/imsSU',$this->htmlMessage))
{
$htmlMsg = $this->htmlMessage;
if($this->contentTransferEncoding == self::CONTENT_TRANSFER_ENCODING_BASE64)
{ $htmlMsg = chunk_split(base64_encode($htmlMsg)); }
}
else
{
$htmlMsg = '<html>'.PHP_EOL;
$htmlMsg .= '<head>'.PHP_EOL;
$htmlMsg .= '<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">'.PHP_EOL;
$htmlMsg .= '</head>'.PHP_EOL;
$htmlMsg .= '<body style="margin-top:0px; margin-bottom:0px; margin-right:0px; margin-left:0px;">'.PHP_EOL;
$htmlMsg .= $this->htmlMessage.PHP_EOL;
$htmlMsg .= '<br><br></body></html>';
if($this->contentTransferEncoding == self::CONTENT_TRANSFER_ENCODING_BASE64)
{ $htmlMsg = chunk_split(base64_encode($htmlMsg)); }
}
$msg .= $htmlMsg; unset($htmlMsg);
$msg .= PHP_EOL.PHP_EOL;
$msg .= '--'.$this->altBoundary."--".PHP_EOL.PHP_EOL;
/*/////////////////////////////////////////*/
/* add images */
/*/////////////////////////////////////////*/
$msg .= implode('',$this->images);
/*/////////////////////////////////////////*/
/* add attachments */
/*/////////////////////////////////////////*/
$msg .= implode('',$this->attachments);
$msg .= '--'.$this->mixedBoundary.'--'.PHP_EOL;
$msg .= '.'.PHP_EOL;
return $msg;
}
catch(Exception $e) { return NULL; }
}
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
/*///////////////////////////////////////////////////////////////////////////////////////*/
/*///////////////////////////////////////////////////////////////////////////////////////*/
class SMTP
{
const VERSION = eMail::VERSION;
protected $SMTPlog;
protected $SMTPserver;
protected $SMTPport;
protected $SMTPconnectionTimeout;
protected $SMTPuser;
protected $SMTPpassword;
/*///////////////////////////////////////////////////////////////////////////////////////*/
public function __construct($SMTPserver = NULL, $SMTPport = NULL, $SMTPuser = NULL, $SMTPpassword = NULL)
{
$this->SMTPServer = $SMTPserver;
$this->SMTPPort = (($SMTPport)?($SMTPport):(25));
$this->SMTPUser = $SMTPuser;
$this->SMTPPassword = $SMTPpassword;
$this->SMTPConnectionTimeout = 30;
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
public function __get($property)
{
switch(strtoupper($property))
{
case 'SERVER':
case 'SMTPSERVER':
return $this->SMTPserver;
case 'PORT':
case 'SMTPPORT':
return $this->SMTPport;
case 'CONNECTIONTIMEOUT':
case 'SMTPCONNECTIONTIMEOUT':
return $this->SMTPconnectionTimeout;
case 'USER':
case 'SMTPUSER':
return $this->SMTPuser;
case 'PASSWORD':
case 'SMTPPASSWORD':
return $this->SMTPpassword;
case 'LOG':
case 'SMTPLOG':
return $this->SMTPlog;
default:
return NULL;
}
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
public function __set($property, $value)
{
switch(strtoupper($property))
{
case 'SERVER':
case 'SMTPSERVER':
return $this->SMTPserver = $value;
case 'PORT':
case 'SMTPPORT':
$value = abs(intval($value));
return $this->SMTPport = ($value)?($value):(25);
case 'CONNECTIONTIMEOUT':
case 'SMTPCONNECTIONTIMEOUT':
$value = abs(intval($value));
return $this->SMTPconnectionTimeout = ($value)?($value):(30);
case 'USER':
case 'SMTPUSER':
return $this->SMTPuser = $value;
case 'PASSWORD':
case 'SMTPPASSWORD':
return $this->SMTPpassword = $value;
default:
return NULL;
}
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
public function send()
{
try
{
$functionArguments = func_get_args(); $emails = array();
array_walk_recursive($functionArguments, create_function('$value, $key, $obj', 'array_push($obj, $value);'), &$emails);
unset($functionArguments);
$this->SMTPlog = array();
$smtpConnect = fsockopen($this->SMTPserver, $this->SMTPport, $errno, $errstr, $this->SMTPconnectionTimeout);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
if(empty($smtpConnect)) { throw new Exception('SMTP connection error!'.(smtpResponse)?(' ('.smtpResponse.')'):('')); }
if($this->SMTPUser)
{
$this->SMTPlog[] = $smtpCommand = 'EHLO '.$this->SMTPserver.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
if(!preg_match('/^250/S', $smtpResponse)) { throw new Exception('Unexpected SMTP error! (SMTP command: "'.$smtpCommand.'" SMTP response: "'.$smtpResponse.'")'); }
$this->SMTPlog[] = $smtpCommand = 'AUTH LOGIN'.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
if(!preg_match('/^334/S', $smtpResponse)) { throw new Exception('Unexpected SMTP error! (SMTP command: "'.$smtpCommand.'" SMTP response: "'.$smtpResponse.'")'); }
$this->SMTPlog[] = $smtpCommand = base64_encode($this->SMTPuser).PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
if(!preg_match('/^334/S', $smtpResponse)) { throw new Exception('Unexpected SMTP error! (SMTP user: "'.$smtpCommand.'" SMTP response: "'.$smtpResponse.'")'); }
$this->SMTPlog[] = $smtpCommand = base64_encode($this->SMTPpassword).PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
if(!preg_match('/^235/S', $smtpResponse)) { throw new Exception('Unexpected SMTP error! (SMTP password: "'.$smtpCommand.'" SMTP response: "'.$smtpResponse.'")'); }
}
else {
$this->SMTPlog[] = $smtpCommand = 'HELO '.$this->SMTPserver.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
if(!preg_match('/^250/S', $smtpResponse)) { throw new Exception('Unexpected SMTP error! (SMTP command: "'.$smtpCommand.'" SMTP response: "'.$smtpResponse.'")'); }
}
foreach($emails as $e)
{
if($e instanceof eMail)
{
$this->SMTPlog[] = $smtpCommand = 'MAIL FROM: '.$e->from->address.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
if(!preg_match('/^250/S', $smtpResponse))
{
$this->SMTPlog[] = $smtpCommand = 'RSET'.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
continue;
}
if(is_array($e->to))
{
foreach($e->to as $rcpt)
{
$this->SMTPlog[] = $smtpCommand = 'RCPT TO: '.$rcpt->address.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
if(!preg_match('/^250/S', $smtpResponse))
{
$this->SMTPlog[] = $smtpCommand = 'RSET'.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
continue;
}
}
}
if(is_array($e->cc))
{
foreach($e->cc as $rcpt)
{
$this->SMTPlog[] = $smtpCommand = 'RCPT TO: '.$rcpt->address.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
if(!preg_match('/^250/S', $smtpResponse))
{
$this->SMTPlog[] = $smtpCommand = 'RSET'.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
continue;
}
}
}
if(is_array($e->bcc))
{
foreach($e->bcc as $rcpt)
{
$this->SMTPlog[] = $smtpCommand = 'RCPT TO: '.$rcpt->address.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
if(!preg_match('/^250/S', $smtpResponse))
{
$this->SMTPlog[] = $smtpCommand = 'RSET'.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
continue;
}
}
}
$this->SMTPlog[] = $smtpCommand = 'DATA'.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
if(!preg_match('/^354/', $smtpResponse))
{
$this->SMTPlog[] = $smtpCommand = 'RSET'.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
continue;
}
$this->SMTPlog[] = $smtpCommand = $e->RawMessage;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
/*Ex: 250 OK id=1QN7Vf-0000vB-8r*/
if(!preg_match('/^250/', $smtpResponse))
{
$this->SMTPlog[] = $smtpCommand = 'RSET'.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
continue;
}
$this->SMTPlog[] = $smtpCommand = 'NOOP'.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
}
}
$this->SMTPlog[] = $smtpCommand = 'QUIT'.PHP_EOL;
fputs($smtpConnect, $smtpCommand);
$this->SMTPlog[] = $smtpResponse = fgets($smtpConnect);
fclose($smtpConnect);
}
catch(Exception $e)
{
if(!empty($smtpConnect)) { try { fclose($smtpConnect); } catch(Exception $ee) { } }
throw $e;
}
}
}
/*///////////////////////////////////////////////////////////////////////////////////////*/
/*///////////////////////////////////////////////////////////////////////////////////////*/
/*///////////////////////////////////////////////////////////////////////////////////////*/
?>