Add Swift Mailer to send mails

This commit is contained in:
frederic34
2016-04-16 18:15:03 +02:00
parent e1d35c2797
commit 567dc1734e
175 changed files with 22383 additions and 11 deletions

View File

@@ -0,0 +1,490 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Sends Messages over SMTP.
*
* @author Chris Corbyn
*/
abstract class Swift_Transport_AbstractSmtpTransport implements Swift_Transport
{
/** Input-Output buffer for sending/receiving SMTP commands and responses */
protected $_buffer;
/** Connection status */
protected $_started = false;
/** The domain name to use in HELO command */
protected $_domain = '[127.0.0.1]';
/** The event dispatching layer */
protected $_eventDispatcher;
/** Source Ip */
protected $_sourceIp;
/** Return an array of params for the Buffer */
abstract protected function _getBufferParams();
/**
* Creates a new EsmtpTransport using the given I/O buffer.
*
* @param Swift_Transport_IoBuffer $buf
* @param Swift_Events_EventDispatcher $dispatcher
*/
public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher)
{
$this->_eventDispatcher = $dispatcher;
$this->_buffer = $buf;
$this->_lookupHostname();
}
/**
* Set the name of the local domain which Swift will identify itself as.
*
* This should be a fully-qualified domain name and should be truly the domain
* you're using.
*
* If your server doesn't have a domain name, use the IP in square
* brackets (i.e. [127.0.0.1]).
*
* @param string $domain
*
* @return Swift_Transport_AbstractSmtpTransport
*/
public function setLocalDomain($domain)
{
$this->_domain = $domain;
return $this;
}
/**
* Get the name of the domain Swift will identify as.
*
* @return string
*/
public function getLocalDomain()
{
return $this->_domain;
}
/**
* Sets the source IP.
*
* @param string $source
*/
public function setSourceIp($source)
{
$this->_sourceIp = $source;
}
/**
* Returns the IP used to connect to the destination.
*
* @return string
*/
public function getSourceIp()
{
return $this->_sourceIp;
}
/**
* Start the SMTP connection.
*/
public function start()
{
if (!$this->_started) {
if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) {
$this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStarted');
if ($evt->bubbleCancelled()) {
return;
}
}
try {
$this->_buffer->initialize($this->_getBufferParams());
} catch (Swift_TransportException $e) {
$this->_throwException($e);
}
$this->_readGreeting();
$this->_doHeloCommand();
if ($evt) {
$this->_eventDispatcher->dispatchEvent($evt, 'transportStarted');
}
$this->_started = true;
}
}
/**
* Test if an SMTP connection has been established.
*
* @return bool
*/
public function isStarted()
{
return $this->_started;
}
/**
* Send the given Message.
*
* Recipient/sender data will be retrieved from the Message API.
* The return value is the number of recipients who were accepted for delivery.
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
$sent = 0;
$failedRecipients = (array) $failedRecipients;
if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) {
$this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
if ($evt->bubbleCancelled()) {
return 0;
}
}
if (!$reversePath = $this->_getReversePath($message)) {
$this->_throwException(new Swift_TransportException(
'Cannot send message without a sender address'
)
);
}
$to = (array) $message->getTo();
$cc = (array) $message->getCc();
$tos = array_merge($to, $cc);
$bcc = (array) $message->getBcc();
$message->setBcc(array());
try {
$sent += $this->_sendTo($message, $reversePath, $tos, $failedRecipients);
$sent += $this->_sendBcc($message, $reversePath, $bcc, $failedRecipients);
} catch (Exception $e) {
$message->setBcc($bcc);
throw $e;
}
$message->setBcc($bcc);
if ($evt) {
if ($sent == count($to) + count($cc) + count($bcc)) {
$evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
} elseif ($sent > 0) {
$evt->setResult(Swift_Events_SendEvent::RESULT_TENTATIVE);
} else {
$evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
}
$evt->setFailedRecipients($failedRecipients);
$this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
}
$message->generateId(); //Make sure a new Message ID is used
return $sent;
}
/**
* Stop the SMTP connection.
*/
public function stop()
{
if ($this->_started) {
if ($evt = $this->_eventDispatcher->createTransportChangeEvent($this)) {
$this->_eventDispatcher->dispatchEvent($evt, 'beforeTransportStopped');
if ($evt->bubbleCancelled()) {
return;
}
}
try {
$this->executeCommand("QUIT\r\n", array(221));
} catch (Swift_TransportException $e) {
}
try {
$this->_buffer->terminate();
if ($evt) {
$this->_eventDispatcher->dispatchEvent($evt, 'transportStopped');
}
} catch (Swift_TransportException $e) {
$this->_throwException($e);
}
}
$this->_started = false;
}
/**
* Register a plugin.
*
* @param Swift_Events_EventListener $plugin
*/
public function registerPlugin(Swift_Events_EventListener $plugin)
{
$this->_eventDispatcher->bindEventListener($plugin);
}
/**
* Reset the current mail transaction.
*/
public function reset()
{
$this->executeCommand("RSET\r\n", array(250));
}
/**
* Get the IoBuffer where read/writes are occurring.
*
* @return Swift_Transport_IoBuffer
*/
public function getBuffer()
{
return $this->_buffer;
}
/**
* Run a command against the buffer, expecting the given response codes.
*
* If no response codes are given, the response will not be validated.
* If codes are given, an exception will be thrown on an invalid response.
*
* @param string $command
* @param int[] $codes
* @param string[] $failures An array of failures by-reference
*
* @return string
*/
public function executeCommand($command, $codes = array(), &$failures = null)
{
$failures = (array) $failures;
$seq = $this->_buffer->write($command);
$response = $this->_getFullResponse($seq);
if ($evt = $this->_eventDispatcher->createCommandEvent($this, $command, $codes)) {
$this->_eventDispatcher->dispatchEvent($evt, 'commandSent');
}
$this->_assertResponseCode($response, $codes);
return $response;
}
/** Read the opening SMTP greeting */
protected function _readGreeting()
{
$this->_assertResponseCode($this->_getFullResponse(0), array(220));
}
/** Send the HELO welcome */
protected function _doHeloCommand()
{
$this->executeCommand(
sprintf("HELO %s\r\n", $this->_domain), array(250)
);
}
/** Send the MAIL FROM command */
protected function _doMailFromCommand($address)
{
$this->executeCommand(
sprintf("MAIL FROM:<%s>\r\n", $address), array(250)
);
}
/** Send the RCPT TO command */
protected function _doRcptToCommand($address)
{
$this->executeCommand(
sprintf("RCPT TO:<%s>\r\n", $address), array(250, 251, 252)
);
}
/** Send the DATA command */
protected function _doDataCommand()
{
$this->executeCommand("DATA\r\n", array(354));
}
/** Stream the contents of the message over the buffer */
protected function _streamMessage(Swift_Mime_Message $message)
{
$this->_buffer->setWriteTranslations(array("\r\n." => "\r\n.."));
try {
$message->toByteStream($this->_buffer);
$this->_buffer->flushBuffers();
} catch (Swift_TransportException $e) {
$this->_throwException($e);
}
$this->_buffer->setWriteTranslations(array());
$this->executeCommand("\r\n.\r\n", array(250));
}
/** Determine the best-use reverse path for this message */
protected function _getReversePath(Swift_Mime_Message $message)
{
$return = $message->getReturnPath();
$sender = $message->getSender();
$from = $message->getFrom();
$path = null;
if (!empty($return)) {
$path = $return;
} elseif (!empty($sender)) {
// Don't use array_keys
reset($sender); // Reset Pointer to first pos
$path = key($sender); // Get key
} elseif (!empty($from)) {
reset($from); // Reset Pointer to first pos
$path = key($from); // Get key
}
return $path;
}
/** Throw a TransportException, first sending it to any listeners */
protected function _throwException(Swift_TransportException $e)
{
if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) {
$this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');
if (!$evt->bubbleCancelled()) {
throw $e;
}
} else {
throw $e;
}
}
/** Throws an Exception if a response code is incorrect */
protected function _assertResponseCode($response, $wanted)
{
list($code) = sscanf($response, '%3d');
$valid = (empty($wanted) || in_array($code, $wanted));
if ($evt = $this->_eventDispatcher->createResponseEvent($this, $response,
$valid)) {
$this->_eventDispatcher->dispatchEvent($evt, 'responseReceived');
}
if (!$valid) {
$this->_throwException(
new Swift_TransportException(
'Expected response code '.implode('/', $wanted).' but got code '.
'"'.$code.'", with message "'.$response.'"',
$code)
);
}
}
/** Get an entire multi-line response using its sequence number */
protected function _getFullResponse($seq)
{
$response = '';
try {
do {
$line = $this->_buffer->readLine($seq);
$response .= $line;
} while (null !== $line && false !== $line && ' ' != $line{3});
} catch (Swift_TransportException $e) {
$this->_throwException($e);
} catch (Swift_IoException $e) {
$this->_throwException(
new Swift_TransportException(
$e->getMessage())
);
}
return $response;
}
/** Send an email to the given recipients from the given reverse path */
private function _doMailTransaction($message, $reversePath, array $recipients, array &$failedRecipients)
{
$sent = 0;
$this->_doMailFromCommand($reversePath);
foreach ($recipients as $forwardPath) {
try {
$this->_doRcptToCommand($forwardPath);
++$sent;
} catch (Swift_TransportException $e) {
$failedRecipients[] = $forwardPath;
}
}
if ($sent != 0) {
$this->_doDataCommand();
$this->_streamMessage($message);
} else {
$this->reset();
}
return $sent;
}
/** Send a message to the given To: recipients */
private function _sendTo(Swift_Mime_Message $message, $reversePath, array $to, array &$failedRecipients)
{
if (empty($to)) {
return 0;
}
return $this->_doMailTransaction($message, $reversePath, array_keys($to),
$failedRecipients);
}
/** Send a message to all Bcc: recipients */
private function _sendBcc(Swift_Mime_Message $message, $reversePath, array $bcc, array &$failedRecipients)
{
$sent = 0;
foreach ($bcc as $forwardPath => $name) {
$message->setBcc(array($forwardPath => $name));
$sent += $this->_doMailTransaction(
$message, $reversePath, array($forwardPath), $failedRecipients
);
}
return $sent;
}
/** Try to determine the hostname of the server this is run on */
private function _lookupHostname()
{
if (!empty($_SERVER['SERVER_NAME'])
&& $this->_isFqdn($_SERVER['SERVER_NAME'])) {
$this->_domain = $_SERVER['SERVER_NAME'];
} elseif (!empty($_SERVER['SERVER_ADDR'])) {
$this->_domain = sprintf('[%s]', $_SERVER['SERVER_ADDR']);
}
}
/** Determine is the $hostname is a fully-qualified name */
private function _isFqdn($hostname)
{
// We could do a really thorough check, but there's really no point
if (false !== $dotPos = strpos($hostname, '.')) {
return ($dotPos > 0) && ($dotPos != strlen($hostname) - 1);
}
return false;
}
/**
* Destructor.
*/
public function __destruct()
{
$this->stop();
}
}

View File

@@ -0,0 +1,81 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles CRAM-MD5 authentication.
*
* @author Chris Corbyn
*/
class Swift_Transport_Esmtp_Auth_CramMd5Authenticator implements Swift_Transport_Esmtp_Authenticator
{
/**
* Get the name of the AUTH mechanism this Authenticator handles.
*
* @return string
*/
public function getAuthKeyword()
{
return 'CRAM-MD5';
}
/**
* Try to authenticate the user with $username and $password.
*
* @param Swift_Transport_SmtpAgent $agent
* @param string $username
* @param string $password
*
* @return bool
*/
public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password)
{
try {
$challenge = $agent->executeCommand("AUTH CRAM-MD5\r\n", array(334));
$challenge = base64_decode(substr($challenge, 4));
$message = base64_encode(
$username.' '.$this->_getResponse($password, $challenge)
);
$agent->executeCommand(sprintf("%s\r\n", $message), array(235));
return true;
} catch (Swift_TransportException $e) {
$agent->executeCommand("RSET\r\n", array(250));
return false;
}
}
/**
* Generate a CRAM-MD5 response from a server challenge.
*
* @param string $secret
* @param string $challenge
*
* @return string
*/
private function _getResponse($secret, $challenge)
{
if (strlen($secret) > 64) {
$secret = pack('H32', md5($secret));
}
if (strlen($secret) < 64) {
$secret = str_pad($secret, 64, chr(0));
}
$k_ipad = substr($secret, 0, 64) ^ str_repeat(chr(0x36), 64);
$k_opad = substr($secret, 0, 64) ^ str_repeat(chr(0x5C), 64);
$inner = pack('H32', md5($k_ipad.$challenge));
$digest = md5($k_opad.$inner);
return $digest;
}
}

View File

@@ -0,0 +1,51 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles LOGIN authentication.
*
* @author Chris Corbyn
*/
class Swift_Transport_Esmtp_Auth_LoginAuthenticator implements Swift_Transport_Esmtp_Authenticator
{
/**
* Get the name of the AUTH mechanism this Authenticator handles.
*
* @return string
*/
public function getAuthKeyword()
{
return 'LOGIN';
}
/**
* Try to authenticate the user with $username and $password.
*
* @param Swift_Transport_SmtpAgent $agent
* @param string $username
* @param string $password
*
* @return bool
*/
public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password)
{
try {
$agent->executeCommand("AUTH LOGIN\r\n", array(334));
$agent->executeCommand(sprintf("%s\r\n", base64_encode($username)), array(334));
$agent->executeCommand(sprintf("%s\r\n", base64_encode($password)), array(235));
return true;
} catch (Swift_TransportException $e) {
$agent->executeCommand("RSET\r\n", array(250));
return false;
}
}
}

View File

@@ -0,0 +1,726 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* This authentication is for Exchange servers. We support version 1 & 2.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles NTLM authentication.
*
* @author Ward Peeters <ward@coding-tech.com>
*/
class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Esmtp_Authenticator
{
const NTLMSIG = "NTLMSSP\x00";
const DESCONST = 'KGS!@#$%';
/**
* Get the name of the AUTH mechanism this Authenticator handles.
*
* @return string
*/
public function getAuthKeyword()
{
return 'NTLM';
}
/**
* Try to authenticate the user with $username and $password.
*
* @param Swift_Transport_SmtpAgent $agent
* @param string $username
* @param string $password
*
* @return bool
*/
public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password)
{
if (!function_exists('mcrypt_module_open')) {
throw new LogicException('The mcrypt functions need to be enabled to use the NTLM authenticator.');
}
if (!function_exists('openssl_random_pseudo_bytes')) {
throw new LogicException('The OpenSSL extension must be enabled to use the NTLM authenticator.');
}
if (!function_exists('bcmul')) {
throw new LogicException('The BCMatch functions must be enabled to use the NTLM authenticator.');
}
try {
// execute AUTH command and filter out the code at the beginning
// AUTH NTLM xxxx
$response = base64_decode(substr(trim($this->sendMessage1($agent)), 4));
// extra parameters for our unit cases
$timestamp = func_num_args() > 3 ? func_get_arg(3) : $this->getCorrectTimestamp(bcmul(microtime(true), '1000'));
$client = func_num_args() > 4 ? func_get_arg(4) : $this->getRandomBytes(8);
// Message 3 response
$this->sendMessage3($response, $username, $password, $timestamp, $client, $agent);
return true;
} catch (Swift_TransportException $e) {
$agent->executeCommand("RSET\r\n", array(250));
return false;
}
}
protected function si2bin($si, $bits = 32)
{
$bin = null;
if ($si >= -pow(2, $bits - 1) && ($si <= pow(2, $bits - 1))) {
// positive or zero
if ($si >= 0) {
$bin = base_convert($si, 10, 2);
// pad to $bits bit
$bin_length = strlen($bin);
if ($bin_length < $bits) {
$bin = str_repeat('0', $bits - $bin_length).$bin;
}
} else {
// negative
$si = -$si - pow(2, $bits);
$bin = base_convert($si, 10, 2);
$bin_length = strlen($bin);
if ($bin_length > $bits) {
$bin = str_repeat('1', $bits - $bin_length).$bin;
}
}
}
return $bin;
}
/**
* Send our auth message and returns the response.
*
* @param Swift_Transport_SmtpAgent $agent
*
* @return string SMTP Response
*/
protected function sendMessage1(Swift_Transport_SmtpAgent $agent)
{
$message = $this->createMessage1();
return $agent->executeCommand(sprintf("AUTH %s %s\r\n", $this->getAuthKeyword(), base64_encode($message)), array(334));
}
/**
* Fetch all details of our response (message 2).
*
* @param string $response
*
* @return array our response parsed
*/
protected function parseMessage2($response)
{
$responseHex = bin2hex($response);
$length = floor(hexdec(substr($responseHex, 28, 4)) / 256) * 2;
$offset = floor(hexdec(substr($responseHex, 32, 4)) / 256) * 2;
$challenge = $this->hex2bin(substr($responseHex, 48, 16));
$context = $this->hex2bin(substr($responseHex, 64, 16));
$targetInfoH = $this->hex2bin(substr($responseHex, 80, 16));
$targetName = $this->hex2bin(substr($responseHex, $offset, $length));
$offset = floor(hexdec(substr($responseHex, 88, 4)) / 256) * 2;
$targetInfoBlock = substr($responseHex, $offset);
list($domainName, $serverName, $DNSDomainName, $DNSServerName, $terminatorByte) = $this->readSubBlock($targetInfoBlock);
return array(
$challenge,
$context,
$targetInfoH,
$targetName,
$domainName,
$serverName,
$DNSDomainName,
$DNSServerName,
$this->hex2bin($targetInfoBlock),
$terminatorByte,
);
}
/**
* Read the blob information in from message2.
*
* @param $block
*
* @return array
*/
protected function readSubBlock($block)
{
// remove terminatorByte cause it's always the same
$block = substr($block, 0, -8);
$length = strlen($block);
$offset = 0;
$data = array();
while ($offset < $length) {
$blockLength = hexdec(substr(substr($block, $offset, 8), -4)) / 256;
$offset += 8;
$data[] = $this->hex2bin(substr($block, $offset, $blockLength * 2));
$offset += $blockLength * 2;
}
if (count($data) == 3) {
$data[] = $data[2];
$data[2] = '';
}
$data[] = $this->createByte('00');
return $data;
}
/**
* Send our final message with all our data.
*
* @param string $response Message 1 response (message 2)
* @param string $username
* @param string $password
* @param string $timestamp
* @param string $client
* @param Swift_Transport_SmtpAgent $agent
* @param bool $v2 Use version2 of the protocol
*
* @return string
*/
protected function sendMessage3($response, $username, $password, $timestamp, $client, Swift_Transport_SmtpAgent $agent, $v2 = true)
{
list($domain, $username) = $this->getDomainAndUsername($username);
//$challenge, $context, $targetInfoH, $targetName, $domainName, $workstation, $DNSDomainName, $DNSServerName, $blob, $ter
list($challenge, , , , , $workstation, , , $blob) = $this->parseMessage2($response);
if (!$v2) {
// LMv1
$lmResponse = $this->createLMPassword($password, $challenge);
// NTLMv1
$ntlmResponse = $this->createNTLMPassword($password, $challenge);
} else {
// LMv2
$lmResponse = $this->createLMv2Password($password, $username, $domain, $challenge, $client);
// NTLMv2
$ntlmResponse = $this->createNTLMv2Hash($password, $username, $domain, $challenge, $blob, $timestamp, $client);
}
$message = $this->createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse);
return $agent->executeCommand(sprintf("%s\r\n", base64_encode($message)), array(235));
}
/**
* Create our message 1.
*
* @return string
*/
protected function createMessage1()
{
return self::NTLMSIG
.$this->createByte('01') // Message 1
.$this->createByte('0702'); // Flags
}
/**
* Create our message 3.
*
* @param string $domain
* @param string $username
* @param string $workstation
* @param string $lmResponse
* @param string $ntlmResponse
*
* @return string
*/
protected function createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse)
{
// Create security buffers
$domainSec = $this->createSecurityBuffer($domain, 64);
$domainInfo = $this->readSecurityBuffer(bin2hex($domainSec));
$userSec = $this->createSecurityBuffer($username, ($domainInfo[0] + $domainInfo[1]) / 2);
$userInfo = $this->readSecurityBuffer(bin2hex($userSec));
$workSec = $this->createSecurityBuffer($workstation, ($userInfo[0] + $userInfo[1]) / 2);
$workInfo = $this->readSecurityBuffer(bin2hex($workSec));
$lmSec = $this->createSecurityBuffer($lmResponse, ($workInfo[0] + $workInfo[1]) / 2, true);
$lmInfo = $this->readSecurityBuffer(bin2hex($lmSec));
$ntlmSec = $this->createSecurityBuffer($ntlmResponse, ($lmInfo[0] + $lmInfo[1]) / 2, true);
return self::NTLMSIG
.$this->createByte('03') // TYPE 3 message
.$lmSec // LM response header
.$ntlmSec // NTLM response header
.$domainSec // Domain header
.$userSec // User header
.$workSec // Workstation header
.$this->createByte('000000009a', 8) // session key header (empty)
.$this->createByte('01020000') // FLAGS
.$this->convertTo16bit($domain) // domain name
.$this->convertTo16bit($username) // username
.$this->convertTo16bit($workstation) // workstation
.$lmResponse
.$ntlmResponse;
}
/**
* @param string $timestamp Epoch timestamp in microseconds
* @param string $client Random bytes
* @param string $targetInfo
*
* @return string
*/
protected function createBlob($timestamp, $client, $targetInfo)
{
return $this->createByte('0101')
.$this->createByte('00')
.$timestamp
.$client
.$this->createByte('00')
.$targetInfo
.$this->createByte('00');
}
/**
* Get domain and username from our username.
*
* @example DOMAIN\username
*
* @param string $name
*
* @return array
*/
protected function getDomainAndUsername($name)
{
if (strpos($name, '\\') !== false) {
return explode('\\', $name);
}
list($user, $domain) = explode('@', $name);
return array($domain, $user);
}
/**
* Create LMv1 response.
*
* @param string $password
* @param string $challenge
*
* @return string
*/
protected function createLMPassword($password, $challenge)
{
// FIRST PART
$password = $this->createByte(strtoupper($password), 14, false);
list($key1, $key2) = str_split($password, 7);
$desKey1 = $this->createDesKey($key1);
$desKey2 = $this->createDesKey($key2);
$constantDecrypt = $this->createByte($this->desEncrypt(self::DESCONST, $desKey1).$this->desEncrypt(self::DESCONST, $desKey2), 21, false);
// SECOND PART
list($key1, $key2, $key3) = str_split($constantDecrypt, 7);
$desKey1 = $this->createDesKey($key1);
$desKey2 = $this->createDesKey($key2);
$desKey3 = $this->createDesKey($key3);
return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3);
}
/**
* Create NTLMv1 response.
*
* @param string $password
* @param string $challenge
*
* @return string
*/
protected function createNTLMPassword($password, $challenge)
{
// FIRST PART
$ntlmHash = $this->createByte($this->md4Encrypt($password), 21, false);
list($key1, $key2, $key3) = str_split($ntlmHash, 7);
$desKey1 = $this->createDesKey($key1);
$desKey2 = $this->createDesKey($key2);
$desKey3 = $this->createDesKey($key3);
return $this->desEncrypt($challenge, $desKey1).$this->desEncrypt($challenge, $desKey2).$this->desEncrypt($challenge, $desKey3);
}
/**
* Convert a normal timestamp to a tenth of a microtime epoch time.
*
* @param string $time
*
* @return string
*/
protected function getCorrectTimestamp($time)
{
// Get our timestamp (tricky!)
bcscale(0);
$time = number_format($time, 0, '.', ''); // save microtime to string
$time = bcadd($time, '11644473600000'); // add epoch time
$time = bcmul($time, 10000); // tenths of a microsecond.
$binary = $this->si2bin($time, 64); // create 64 bit binary string
$timestamp = '';
for ($i = 0; $i < 8; ++$i) {
$timestamp .= chr(bindec(substr($binary, -(($i + 1) * 8), 8)));
}
return $timestamp;
}
/**
* Create LMv2 response.
*
* @param string $password
* @param string $username
* @param string $domain
* @param string $challenge NTLM Challenge
* @param string $client Random string
*
* @return string
*/
protected function createLMv2Password($password, $username, $domain, $challenge, $client)
{
$lmPass = '00'; // by default 00
// if $password > 15 than we can't use this method
if (strlen($password) <= 15) {
$ntlmHash = $this->md4Encrypt($password);
$ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain));
$lmPass = bin2hex($this->md5Encrypt($ntml2Hash, $challenge.$client).$client);
}
return $this->createByte($lmPass, 24);
}
/**
* Create NTLMv2 response.
*
* @param string $password
* @param string $username
* @param string $domain
* @param string $challenge Hex values
* @param string $targetInfo Hex values
* @param string $timestamp
* @param string $client Random bytes
*
* @return string
*
* @see http://davenport.sourceforge.net/ntlm.html#theNtlmResponse
*/
protected function createNTLMv2Hash($password, $username, $domain, $challenge, $targetInfo, $timestamp, $client)
{
$ntlmHash = $this->md4Encrypt($password);
$ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username).$domain));
// create blob
$blob = $this->createBlob($timestamp, $client, $targetInfo);
$ntlmv2Response = $this->md5Encrypt($ntml2Hash, $challenge.$blob);
return $ntlmv2Response.$blob;
}
protected function createDesKey($key)
{
$material = array(bin2hex($key[0]));
$len = strlen($key);
for ($i = 1; $i < $len; ++$i) {
list($high, $low) = str_split(bin2hex($key[$i]));
$v = $this->castToByte(ord($key[$i - 1]) << (7 + 1 - $i) | $this->uRShift(hexdec(dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xf)), $i));
$material[] = str_pad(substr(dechex($v), -2), 2, '0', STR_PAD_LEFT); // cast to byte
}
$material[] = str_pad(substr(dechex($this->castToByte(ord($key[6]) << 1)), -2), 2, '0');
// odd parity
foreach ($material as $k => $v) {
$b = $this->castToByte(hexdec($v));
$needsParity = (($this->uRShift($b, 7) ^ $this->uRShift($b, 6) ^ $this->uRShift($b, 5)
^ $this->uRShift($b, 4) ^ $this->uRShift($b, 3) ^ $this->uRShift($b, 2)
^ $this->uRShift($b, 1)) & 0x01) == 0;
list($high, $low) = str_split($v);
if ($needsParity) {
$material[$k] = dechex(hexdec($high) | 0x0).dechex(hexdec($low) | 0x1);
} else {
$material[$k] = dechex(hexdec($high) & 0xf).dechex(hexdec($low) & 0xe);
}
}
return $this->hex2bin(implode('', $material));
}
/** HELPER FUNCTIONS */
/**
* Create our security buffer depending on length and offset.
*
* @param string $value Value we want to put in
* @param int $offset start of value
* @param bool $is16 Do we 16bit string or not?
*
* @return string
*/
protected function createSecurityBuffer($value, $offset, $is16 = false)
{
$length = strlen(bin2hex($value));
$length = $is16 ? $length / 2 : $length;
$length = $this->createByte(str_pad(dechex($length), 2, '0', STR_PAD_LEFT), 2);
return $length.$length.$this->createByte(dechex($offset), 4);
}
/**
* Read our security buffer to fetch length and offset of our value.
*
* @param string $value Securitybuffer in hex
*
* @return array array with length and offset
*/
protected function readSecurityBuffer($value)
{
$length = floor(hexdec(substr($value, 0, 4)) / 256) * 2;
$offset = floor(hexdec(substr($value, 8, 4)) / 256) * 2;
return array($length, $offset);
}
/**
* Cast to byte java equivalent to (byte).
*
* @param int $v
*
* @return int
*/
protected function castToByte($v)
{
return (($v + 128) % 256) - 128;
}
/**
* Java unsigned right bitwise
* $a >>> $b.
*
* @param int $a
* @param int $b
*
* @return int
*/
protected function uRShift($a, $b)
{
if ($b == 0) {
return $a;
}
return ($a >> $b) & ~(1 << (8 * PHP_INT_SIZE - 1) >> ($b - 1));
}
/**
* Right padding with 0 to certain length.
*
* @param string $input
* @param int $bytes Length of bytes
* @param bool $isHex Did we provided hex value
*
* @return string
*/
protected function createByte($input, $bytes = 4, $isHex = true)
{
if ($isHex) {
$byte = $this->hex2bin(str_pad($input, $bytes * 2, '00'));
} else {
$byte = str_pad($input, $bytes, "\x00");
}
return $byte;
}
/**
* Create random bytes.
*
* @param $length
*
* @return string
*/
protected function getRandomBytes($length)
{
$bytes = openssl_random_pseudo_bytes($length, $strong);
if (false !== $bytes && true === $strong) {
return $bytes;
}
throw new RuntimeException('OpenSSL did not produce a secure random number.');
}
/** ENCRYPTION ALGORITHMS */
/**
* DES Encryption.
*
* @param string $value
* @param string $key
*
* @return string
*/
protected function desEncrypt($value, $key)
{
$cipher = mcrypt_module_open(MCRYPT_DES, '', 'ecb', '');
mcrypt_generic_init($cipher, $key, mcrypt_create_iv(mcrypt_enc_get_iv_size($cipher), MCRYPT_DEV_RANDOM));
return mcrypt_generic($cipher, $value);
}
/**
* MD5 Encryption.
*
* @param string $key Encryption key
* @param string $msg Message to encrypt
*
* @return string
*/
protected function md5Encrypt($key, $msg)
{
$blocksize = 64;
if (strlen($key) > $blocksize) {
$key = pack('H*', md5($key));
}
$key = str_pad($key, $blocksize, "\0");
$ipadk = $key ^ str_repeat("\x36", $blocksize);
$opadk = $key ^ str_repeat("\x5c", $blocksize);
return pack('H*', md5($opadk.pack('H*', md5($ipadk.$msg))));
}
/**
* MD4 Encryption.
*
* @param string $input
*
* @return string
*
* @see http://php.net/manual/en/ref.hash.php
*/
protected function md4Encrypt($input)
{
$input = $this->convertTo16bit($input);
return function_exists('hash') ? $this->hex2bin(hash('md4', $input)) : mhash(MHASH_MD4, $input);
}
/**
* Convert UTF-8 to UTF-16.
*
* @param string $input
*
* @return string
*/
protected function convertTo16bit($input)
{
return iconv('UTF-8', 'UTF-16LE', $input);
}
/**
* Hex2bin replacement for < PHP 5.4.
*
* @param string $hex
*
* @return string Binary
*/
protected function hex2bin($hex)
{
if (function_exists('hex2bin')) {
return hex2bin($hex);
} else {
return pack('H*', $hex);
}
}
/**
* @param string $message
*/
protected function debug($message)
{
$message = bin2hex($message);
$messageId = substr($message, 16, 8);
echo substr($message, 0, 16)." NTLMSSP Signature<br />\n";
echo $messageId." Type Indicator<br />\n";
if ($messageId == '02000000') {
$map = array(
'Challenge',
'Context',
'Target Information Security Buffer',
'Target Name Data',
'NetBIOS Domain Name',
'NetBIOS Server Name',
'DNS Domain Name',
'DNS Server Name',
'BLOB',
'Target Information Terminator',
);
$data = $this->parseMessage2($this->hex2bin($message));
foreach ($map as $key => $value) {
echo bin2hex($data[$key]).' - '.$data[$key].' ||| '.$value."<br />\n";
}
} elseif ($messageId == '03000000') {
$i = 0;
$data[$i++] = substr($message, 24, 16);
list($lmLength, $lmOffset) = $this->readSecurityBuffer($data[$i - 1]);
$data[$i++] = substr($message, 40, 16);
list($ntmlLength, $ntmlOffset) = $this->readSecurityBuffer($data[$i - 1]);
$data[$i++] = substr($message, 56, 16);
list($targetLength, $targetOffset) = $this->readSecurityBuffer($data[$i - 1]);
$data[$i++] = substr($message, 72, 16);
list($userLength, $userOffset) = $this->readSecurityBuffer($data[$i - 1]);
$data[$i++] = substr($message, 88, 16);
list($workLength, $workOffset) = $this->readSecurityBuffer($data[$i - 1]);
$data[$i++] = substr($message, 104, 16);
$data[$i++] = substr($message, 120, 8);
$data[$i++] = substr($message, $targetOffset, $targetLength);
$data[$i++] = substr($message, $userOffset, $userLength);
$data[$i++] = substr($message, $workOffset, $workLength);
$data[$i++] = substr($message, $lmOffset, $lmLength);
$data[$i] = substr($message, $ntmlOffset, $ntmlLength);
$map = array(
'LM Response Security Buffer',
'NTLM Response Security Buffer',
'Target Name Security Buffer',
'User Name Security Buffer',
'Workstation Name Security Buffer',
'Session Key Security Buffer',
'Flags',
'Target Name Data',
'User Name Data',
'Workstation Name Data',
'LM Response Data',
'NTLM Response Data',
);
foreach ($map as $key => $value) {
echo $data[$key].' - '.$this->hex2bin($data[$key]).' ||| '.$value."<br />\n";
}
}
echo '<br /><br />';
}
}

View File

@@ -0,0 +1,50 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles PLAIN authentication.
*
* @author Chris Corbyn
*/
class Swift_Transport_Esmtp_Auth_PlainAuthenticator implements Swift_Transport_Esmtp_Authenticator
{
/**
* Get the name of the AUTH mechanism this Authenticator handles.
*
* @return string
*/
public function getAuthKeyword()
{
return 'PLAIN';
}
/**
* Try to authenticate the user with $username and $password.
*
* @param Swift_Transport_SmtpAgent $agent
* @param string $username
* @param string $password
*
* @return bool
*/
public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password)
{
try {
$message = base64_encode($username.chr(0).$username.chr(0).$password);
$agent->executeCommand(sprintf("AUTH PLAIN %s\r\n", $message), array(235));
return true;
} catch (Swift_TransportException $e) {
$agent->executeCommand("RSET\r\n", array(250));
return false;
}
}
}

View File

@@ -0,0 +1,70 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Handles XOAUTH2 authentication.
*
* Example:
* <code>
* $transport = Swift_SmtpTransport::newInstance('smtp.gmail.com', 587, 'tls')
* ->setAuthMode('XOAUTH2')
* ->setUsername('YOUR_EMAIL_ADDRESS')
* ->setPassword('YOUR_ACCESS_TOKEN');
* </code>
*
* @author xu.li<AthenaLightenedMyPath@gmail.com>
*
* @see https://developers.google.com/google-apps/gmail/xoauth2_protocol
*/
class Swift_Transport_Esmtp_Auth_XOAuth2Authenticator implements Swift_Transport_Esmtp_Authenticator
{
/**
* Get the name of the AUTH mechanism this Authenticator handles.
*
* @return string
*/
public function getAuthKeyword()
{
return 'XOAUTH2';
}
/**
* Try to authenticate the user with $email and $token.
*
* @param Swift_Transport_SmtpAgent $agent
* @param string $email
* @param string $token
*
* @return bool
*/
public function authenticate(Swift_Transport_SmtpAgent $agent, $email, $token)
{
try {
$param = $this->constructXOAuth2Params($email, $token);
$agent->executeCommand('AUTH XOAUTH2 '.$param."\r\n", array(235));
return true;
} catch (Swift_TransportException $e) {
$agent->executeCommand("RSET\r\n", array(250));
return false;
}
}
/**
* Construct the auth parameter.
*
* @see https://developers.google.com/google-apps/gmail/xoauth2_protocol#the_sasl_xoauth2_mechanism
*/
protected function constructXOAuth2Params($email, $token)
{
return base64_encode("user=$email\1auth=Bearer $token\1\1");
}
}

View File

@@ -0,0 +1,263 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An ESMTP handler for AUTH support.
*
* @author Chris Corbyn
*/
class Swift_Transport_Esmtp_AuthHandler implements Swift_Transport_EsmtpHandler
{
/**
* Authenticators available to process the request.
*
* @var Swift_Transport_Esmtp_Authenticator[]
*/
private $_authenticators = array();
/**
* The username for authentication.
*
* @var string
*/
private $_username;
/**
* The password for authentication.
*
* @var string
*/
private $_password;
/**
* The auth mode for authentication.
*
* @var string
*/
private $_auth_mode;
/**
* The ESMTP AUTH parameters available.
*
* @var string[]
*/
private $_esmtpParams = array();
/**
* Create a new AuthHandler with $authenticators for support.
*
* @param Swift_Transport_Esmtp_Authenticator[] $authenticators
*/
public function __construct(array $authenticators)
{
$this->setAuthenticators($authenticators);
}
/**
* Set the Authenticators which can process a login request.
*
* @param Swift_Transport_Esmtp_Authenticator[] $authenticators
*/
public function setAuthenticators(array $authenticators)
{
$this->_authenticators = $authenticators;
}
/**
* Get the Authenticators which can process a login request.
*
* @return Swift_Transport_Esmtp_Authenticator[]
*/
public function getAuthenticators()
{
return $this->_authenticators;
}
/**
* Set the username to authenticate with.
*
* @param string $username
*/
public function setUsername($username)
{
$this->_username = $username;
}
/**
* Get the username to authenticate with.
*
* @return string
*/
public function getUsername()
{
return $this->_username;
}
/**
* Set the password to authenticate with.
*
* @param string $password
*/
public function setPassword($password)
{
$this->_password = $password;
}
/**
* Get the password to authenticate with.
*
* @return string
*/
public function getPassword()
{
return $this->_password;
}
/**
* Set the auth mode to use to authenticate.
*
* @param string $mode
*/
public function setAuthMode($mode)
{
$this->_auth_mode = $mode;
}
/**
* Get the auth mode to use to authenticate.
*
* @return string
*/
public function getAuthMode()
{
return $this->_auth_mode;
}
/**
* Get the name of the ESMTP extension this handles.
*
* @return bool
*/
public function getHandledKeyword()
{
return 'AUTH';
}
/**
* Set the parameters which the EHLO greeting indicated.
*
* @param string[] $parameters
*/
public function setKeywordParams(array $parameters)
{
$this->_esmtpParams = $parameters;
}
/**
* Runs immediately after a EHLO has been issued.
*
* @param Swift_Transport_SmtpAgent $agent to read/write
*/
public function afterEhlo(Swift_Transport_SmtpAgent $agent)
{
if ($this->_username) {
$count = 0;
foreach ($this->_getAuthenticatorsForAgent() as $authenticator) {
if (in_array(strtolower($authenticator->getAuthKeyword()),
array_map('strtolower', $this->_esmtpParams))) {
++$count;
if ($authenticator->authenticate($agent, $this->_username, $this->_password)) {
return;
}
}
}
throw new Swift_TransportException(
'Failed to authenticate on SMTP server with username "'.
$this->_username.'" using '.$count.' possible authenticators'
);
}
}
/**
* Not used.
*/
public function getMailParams()
{
return array();
}
/**
* Not used.
*/
public function getRcptParams()
{
return array();
}
/**
* Not used.
*/
public function onCommand(Swift_Transport_SmtpAgent $agent, $command, $codes = array(), &$failedRecipients = null, &$stop = false)
{
}
/**
* Returns +1, -1 or 0 according to the rules for usort().
*
* This method is called to ensure extensions can be execute in an appropriate order.
*
* @param string $esmtpKeyword to compare with
*
* @return int
*/
public function getPriorityOver($esmtpKeyword)
{
return 0;
}
/**
* Returns an array of method names which are exposed to the Esmtp class.
*
* @return string[]
*/
public function exposeMixinMethods()
{
return array('setUsername', 'getUsername', 'setPassword', 'getPassword', 'setAuthMode', 'getAuthMode');
}
/**
* Not used.
*/
public function resetState()
{
}
/**
* Returns the authenticator list for the given agent.
*
* @param Swift_Transport_SmtpAgent $agent
*
* @return array
*/
protected function _getAuthenticatorsForAgent()
{
if (!$mode = strtolower($this->_auth_mode)) {
return $this->_authenticators;
}
foreach ($this->_authenticators as $authenticator) {
if (strtolower($authenticator->getAuthKeyword()) == $mode) {
return array($authenticator);
}
}
throw new Swift_TransportException('Auth mode '.$mode.' is invalid');
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An Authentication mechanism.
*
* @author Chris Corbyn
*/
interface Swift_Transport_Esmtp_Authenticator
{
/**
* Get the name of the AUTH mechanism this Authenticator handles.
*
* @return string
*/
public function getAuthKeyword();
/**
* Try to authenticate the user with $username and $password.
*
* @param Swift_Transport_SmtpAgent $agent
* @param string $username
* @param string $password
*
* @return bool
*/
public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password);
}

View File

@@ -0,0 +1,86 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* An ESMTP handler.
*
* @author Chris Corbyn
*/
interface Swift_Transport_EsmtpHandler
{
/**
* Get the name of the ESMTP extension this handles.
*
* @return bool
*/
public function getHandledKeyword();
/**
* Set the parameters which the EHLO greeting indicated.
*
* @param string[] $parameters
*/
public function setKeywordParams(array $parameters);
/**
* Runs immediately after a EHLO has been issued.
*
* @param Swift_Transport_SmtpAgent $agent to read/write
*/
public function afterEhlo(Swift_Transport_SmtpAgent $agent);
/**
* Get params which are appended to MAIL FROM:<>.
*
* @return string[]
*/
public function getMailParams();
/**
* Get params which are appended to RCPT TO:<>.
*
* @return string[]
*/
public function getRcptParams();
/**
* Runs when a command is due to be sent.
*
* @param Swift_Transport_SmtpAgent $agent to read/write
* @param string $command to send
* @param int[] $codes expected in response
* @param string[] $failedRecipients to collect failures
* @param bool $stop to be set true by-reference if the command is now sent
*/
public function onCommand(Swift_Transport_SmtpAgent $agent, $command, $codes = array(), &$failedRecipients = null, &$stop = false);
/**
* Returns +1, -1 or 0 according to the rules for usort().
*
* This method is called to ensure extensions can be execute in an appropriate order.
*
* @param string $esmtpKeyword to compare with
*
* @return int
*/
public function getPriorityOver($esmtpKeyword);
/**
* Returns an array of method names which are exposed to the Esmtp class.
*
* @return string[]
*/
public function exposeMixinMethods();
/**
* Tells this handler to clear any buffers and reset its state.
*/
public function resetState();
}

View File

@@ -0,0 +1,412 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Sends Messages over SMTP with ESMTP support.
*
* @author Chris Corbyn
*/
class Swift_Transport_EsmtpTransport extends Swift_Transport_AbstractSmtpTransport implements Swift_Transport_SmtpAgent
{
/**
* ESMTP extension handlers.
*
* @var Swift_Transport_EsmtpHandler[]
*/
private $_handlers = array();
/**
* ESMTP capabilities.
*
* @var string[]
*/
private $_capabilities = array();
/**
* Connection buffer parameters.
*
* @var array
*/
private $_params = array(
'protocol' => 'tcp',
'host' => 'localhost',
'port' => 25,
'timeout' => 30,
'blocking' => 1,
'tls' => false,
'type' => Swift_Transport_IoBuffer::TYPE_SOCKET,
'stream_context_options' => array(),
);
/**
* Creates a new EsmtpTransport using the given I/O buffer.
*
* @param Swift_Transport_IoBuffer $buf
* @param Swift_Transport_EsmtpHandler[] $extensionHandlers
* @param Swift_Events_EventDispatcher $dispatcher
*/
public function __construct(Swift_Transport_IoBuffer $buf, array $extensionHandlers, Swift_Events_EventDispatcher $dispatcher)
{
parent::__construct($buf, $dispatcher);
$this->setExtensionHandlers($extensionHandlers);
}
/**
* Set the host to connect to.
*
* @param string $host
*
* @return Swift_Transport_EsmtpTransport
*/
public function setHost($host)
{
$this->_params['host'] = $host;
return $this;
}
/**
* Get the host to connect to.
*
* @return string
*/
public function getHost()
{
return $this->_params['host'];
}
/**
* Set the port to connect to.
*
* @param int $port
*
* @return Swift_Transport_EsmtpTransport
*/
public function setPort($port)
{
$this->_params['port'] = (int) $port;
return $this;
}
/**
* Get the port to connect to.
*
* @return int
*/
public function getPort()
{
return $this->_params['port'];
}
/**
* Set the connection timeout.
*
* @param int $timeout seconds
*
* @return Swift_Transport_EsmtpTransport
*/
public function setTimeout($timeout)
{
$this->_params['timeout'] = (int) $timeout;
$this->_buffer->setParam('timeout', (int) $timeout);
return $this;
}
/**
* Get the connection timeout.
*
* @return int
*/
public function getTimeout()
{
return $this->_params['timeout'];
}
/**
* Set the encryption type (tls or ssl).
*
* @param string $encryption
*
* @return Swift_Transport_EsmtpTransport
*/
public function setEncryption($encryption)
{
if ('tls' == $encryption) {
$this->_params['protocol'] = 'tcp';
$this->_params['tls'] = true;
} else {
$this->_params['protocol'] = $encryption;
$this->_params['tls'] = false;
}
return $this;
}
/**
* Get the encryption type.
*
* @return string
*/
public function getEncryption()
{
return $this->_params['tls'] ? 'tls' : $this->_params['protocol'];
}
/**
* Sets the stream context options.
*
* @param array $options
*
* @return Swift_Transport_EsmtpTransport
*/
public function setStreamOptions($options)
{
$this->_params['stream_context_options'] = $options;
return $this;
}
/**
* Returns the stream context options.
*
* @return array
*/
public function getStreamOptions()
{
return $this->_params['stream_context_options'];
}
/**
* Sets the source IP.
*
* @param string $source
*
* @return Swift_Transport_EsmtpTransport
*/
public function setSourceIp($source)
{
$this->_params['sourceIp'] = $source;
return $this;
}
/**
* Returns the IP used to connect to the destination.
*
* @return string
*/
public function getSourceIp()
{
return isset($this->_params['sourceIp']) ? $this->_params['sourceIp'] : null;
}
/**
* Set ESMTP extension handlers.
*
* @param Swift_Transport_EsmtpHandler[] $handlers
*
* @return Swift_Transport_EsmtpTransport
*/
public function setExtensionHandlers(array $handlers)
{
$assoc = array();
foreach ($handlers as $handler) {
$assoc[$handler->getHandledKeyword()] = $handler;
}
@uasort($assoc, array($this, '_sortHandlers'));
$this->_handlers = $assoc;
$this->_setHandlerParams();
return $this;
}
/**
* Get ESMTP extension handlers.
*
* @return Swift_Transport_EsmtpHandler[]
*/
public function getExtensionHandlers()
{
return array_values($this->_handlers);
}
/**
* Run a command against the buffer, expecting the given response codes.
*
* If no response codes are given, the response will not be validated.
* If codes are given, an exception will be thrown on an invalid response.
*
* @param string $command
* @param int[] $codes
* @param string[] $failures An array of failures by-reference
*
* @return string
*/
public function executeCommand($command, $codes = array(), &$failures = null)
{
$failures = (array) $failures;
$stopSignal = false;
$response = null;
foreach ($this->_getActiveHandlers() as $handler) {
$response = $handler->onCommand(
$this, $command, $codes, $failures, $stopSignal
);
if ($stopSignal) {
return $response;
}
}
return parent::executeCommand($command, $codes, $failures);
}
// -- Mixin invocation code
/** Mixin handling method for ESMTP handlers */
public function __call($method, $args)
{
foreach ($this->_handlers as $handler) {
if (in_array(strtolower($method),
array_map('strtolower', (array) $handler->exposeMixinMethods())
)) {
$return = call_user_func_array(array($handler, $method), $args);
// Allow fluid method calls
if (is_null($return) && substr($method, 0, 3) == 'set') {
return $this;
} else {
return $return;
}
}
}
trigger_error('Call to undefined method '.$method, E_USER_ERROR);
}
/** Get the params to initialize the buffer */
protected function _getBufferParams()
{
return $this->_params;
}
/** Overridden to perform EHLO instead */
protected function _doHeloCommand()
{
try {
$response = $this->executeCommand(
sprintf("EHLO %s\r\n", $this->_domain), array(250)
);
} catch (Swift_TransportException $e) {
return parent::_doHeloCommand();
}
if ($this->_params['tls']) {
try {
$this->executeCommand("STARTTLS\r\n", array(220));
if (!$this->_buffer->startTLS()) {
throw new Swift_TransportException('Unable to connect with TLS encryption');
}
try {
$response = $this->executeCommand(
sprintf("EHLO %s\r\n", $this->_domain), array(250)
);
} catch (Swift_TransportException $e) {
return parent::_doHeloCommand();
}
} catch (Swift_TransportException $e) {
$this->_throwException($e);
}
}
$this->_capabilities = $this->_getCapabilities($response);
$this->_setHandlerParams();
foreach ($this->_getActiveHandlers() as $handler) {
$handler->afterEhlo($this);
}
}
/** Overridden to add Extension support */
protected function _doMailFromCommand($address)
{
$handlers = $this->_getActiveHandlers();
$params = array();
foreach ($handlers as $handler) {
$params = array_merge($params, (array) $handler->getMailParams());
}
$paramStr = !empty($params) ? ' '.implode(' ', $params) : '';
$this->executeCommand(
sprintf("MAIL FROM:<%s>%s\r\n", $address, $paramStr), array(250)
);
}
/** Overridden to add Extension support */
protected function _doRcptToCommand($address)
{
$handlers = $this->_getActiveHandlers();
$params = array();
foreach ($handlers as $handler) {
$params = array_merge($params, (array) $handler->getRcptParams());
}
$paramStr = !empty($params) ? ' '.implode(' ', $params) : '';
$this->executeCommand(
sprintf("RCPT TO:<%s>%s\r\n", $address, $paramStr), array(250, 251, 252)
);
}
/** Determine ESMTP capabilities by function group */
private function _getCapabilities($ehloResponse)
{
$capabilities = array();
$ehloResponse = trim($ehloResponse);
$lines = explode("\r\n", $ehloResponse);
array_shift($lines);
foreach ($lines as $line) {
if (preg_match('/^[0-9]{3}[ -]([A-Z0-9-]+)((?:[ =].*)?)$/Di', $line, $matches)) {
$keyword = strtoupper($matches[1]);
$paramStr = strtoupper(ltrim($matches[2], ' ='));
$params = !empty($paramStr) ? explode(' ', $paramStr) : array();
$capabilities[$keyword] = $params;
}
}
return $capabilities;
}
/** Set parameters which are used by each extension handler */
private function _setHandlerParams()
{
foreach ($this->_handlers as $keyword => $handler) {
if (array_key_exists($keyword, $this->_capabilities)) {
$handler->setKeywordParams($this->_capabilities[$keyword]);
}
}
}
/** Get ESMTP handlers which are currently ok to use */
private function _getActiveHandlers()
{
$handlers = array();
foreach ($this->_handlers as $keyword => $handler) {
if (array_key_exists($keyword, $this->_capabilities)) {
$handlers[] = $handler;
}
}
return $handlers;
}
/** Custom sort for extension handler ordering */
private function _sortHandlers($a, $b)
{
return $a->getPriorityOver($b->getHandledKeyword());
}
}

View File

@@ -0,0 +1,82 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Contains a list of redundant Transports so when one fails, the next is used.
*
* @author Chris Corbyn
*/
class Swift_Transport_FailoverTransport extends Swift_Transport_LoadBalancedTransport
{
/**
* Registered transport currently used.
*
* @var Swift_Transport
*/
private $_currentTransport;
/**
* Send the given Message.
*
* Recipient/sender data will be retrieved from the Message API.
* The return value is the number of recipients who were accepted for delivery.
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
$maxTransports = count($this->_transports);
$sent = 0;
$this->_lastUsedTransport = null;
for ($i = 0; $i < $maxTransports
&& $transport = $this->_getNextTransport(); ++$i) {
try {
if (!$transport->isStarted()) {
$transport->start();
}
if ($sent = $transport->send($message, $failedRecipients)) {
$this->_lastUsedTransport = $transport;
return $sent;
}
} catch (Swift_TransportException $e) {
$this->_killCurrentTransport();
}
}
if (count($this->_transports) == 0) {
throw new Swift_TransportException(
'All Transports in FailoverTransport failed, or no Transports available'
);
}
return $sent;
}
protected function _getNextTransport()
{
if (!isset($this->_currentTransport)) {
$this->_currentTransport = parent::_getNextTransport();
}
return $this->_currentTransport;
}
protected function _killCurrentTransport()
{
$this->_currentTransport = null;
parent::_killCurrentTransport();
}
}

View File

@@ -0,0 +1,67 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Buffers input and output to a resource.
*
* @author Chris Corbyn
*/
interface Swift_Transport_IoBuffer extends Swift_InputByteStream, Swift_OutputByteStream
{
/** A socket buffer over TCP */
const TYPE_SOCKET = 0x0001;
/** A process buffer with I/O support */
const TYPE_PROCESS = 0x0010;
/**
* Perform any initialization needed, using the given $params.
*
* Parameters will vary depending upon the type of IoBuffer used.
*
* @param array $params
*/
public function initialize(array $params);
/**
* Set an individual param on the buffer (e.g. switching to SSL).
*
* @param string $param
* @param mixed $value
*/
public function setParam($param, $value);
/**
* Perform any shutdown logic needed.
*/
public function terminate();
/**
* Set an array of string replacements which should be made on data written
* to the buffer.
*
* This could replace LF with CRLF for example.
*
* @param string[] $replacements
*/
public function setWriteTranslations(array $replacements);
/**
* Get a line of output (including any CRLF).
*
* The $sequence number comes from any writes and may or may not be used
* depending upon the implementation.
*
* @param int $sequence of last write to scan from
*
* @return string
*/
public function readLine($sequence);
}

View File

@@ -0,0 +1,178 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Redundantly and rotationally uses several Transports when sending.
*
* @author Chris Corbyn
*/
class Swift_Transport_LoadBalancedTransport implements Swift_Transport
{
/**
* Transports which are deemed useless.
*
* @var Swift_Transport[]
*/
private $_deadTransports = array();
/**
* The Transports which are used in rotation.
*
* @var Swift_Transport[]
*/
protected $_transports = array();
/**
* The Transport used in the last successful send operation.
*
* @var Swift_Transport
*/
protected $_lastUsedTransport = null;
/**
* Set $transports to delegate to.
*
* @param Swift_Transport[] $transports
*/
public function setTransports(array $transports)
{
$this->_transports = $transports;
$this->_deadTransports = array();
}
/**
* Get $transports to delegate to.
*
* @return Swift_Transport[]
*/
public function getTransports()
{
return array_merge($this->_transports, $this->_deadTransports);
}
/**
* Get the Transport used in the last successful send operation.
*
* @return Swift_Transport
*/
public function getLastUsedTransport()
{
return $this->_lastUsedTransport;
}
/**
* Test if this Transport mechanism has started.
*
* @return bool
*/
public function isStarted()
{
return count($this->_transports) > 0;
}
/**
* Start this Transport mechanism.
*/
public function start()
{
$this->_transports = array_merge($this->_transports, $this->_deadTransports);
}
/**
* Stop this Transport mechanism.
*/
public function stop()
{
foreach ($this->_transports as $transport) {
$transport->stop();
}
}
/**
* Send the given Message.
*
* Recipient/sender data will be retrieved from the Message API.
* The return value is the number of recipients who were accepted for delivery.
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
$maxTransports = count($this->_transports);
$sent = 0;
$this->_lastUsedTransport = null;
for ($i = 0; $i < $maxTransports
&& $transport = $this->_getNextTransport(); ++$i) {
try {
if (!$transport->isStarted()) {
$transport->start();
}
if ($sent = $transport->send($message, $failedRecipients)) {
$this->_lastUsedTransport = $transport;
break;
}
} catch (Swift_TransportException $e) {
$this->_killCurrentTransport();
}
}
if (count($this->_transports) == 0) {
throw new Swift_TransportException(
'All Transports in LoadBalancedTransport failed, or no Transports available'
);
}
return $sent;
}
/**
* Register a plugin.
*
* @param Swift_Events_EventListener $plugin
*/
public function registerPlugin(Swift_Events_EventListener $plugin)
{
foreach ($this->_transports as $transport) {
$transport->registerPlugin($plugin);
}
}
/**
* Rotates the transport list around and returns the first instance.
*
* @return Swift_Transport
*/
protected function _getNextTransport()
{
if ($next = array_shift($this->_transports)) {
$this->_transports[] = $next;
}
return $next;
}
/**
* Tag the currently used (top of stack) transport as dead/useless.
*/
protected function _killCurrentTransport()
{
if ($transport = array_pop($this->_transports)) {
try {
$transport->stop();
} catch (Exception $e) {
}
$this->_deadTransports[] = $transport;
}
}
}

View File

@@ -0,0 +1,32 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* This interface intercepts calls to the mail() function.
*
* @author Chris Corbyn
*/
interface Swift_Transport_MailInvoker
{
/**
* Send mail via the mail() function.
*
* This method takes the same arguments as PHP mail().
*
* @param string $to
* @param string $subject
* @param string $body
* @param string $headers
* @param string $extraParams
*
* @return bool
*/
public function mail($to, $subject, $body, $headers = null, $extraParams = null);
}

View File

@@ -0,0 +1,239 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Sends Messages using the mail() function.
*
* It is advised that users do not use this transport if at all possible
* since a number of plugin features cannot be used in conjunction with this
* transport due to the internal interface in PHP itself.
*
* The level of error reporting with this transport is incredibly weak, again
* due to limitations of PHP's internal mail() function. You'll get an
* all-or-nothing result from sending.
*
* @author Chris Corbyn
*/
class Swift_Transport_MailTransport implements Swift_Transport
{
/** Additional parameters to pass to mail() */
private $_extraParams = '-f%s';
/** The event dispatcher from the plugin API */
private $_eventDispatcher;
/** An invoker that calls the mail() function */
private $_invoker;
/**
* Create a new MailTransport with the $log.
*
* @param Swift_Transport_MailInvoker $invoker
* @param Swift_Events_EventDispatcher $eventDispatcher
*/
public function __construct(Swift_Transport_MailInvoker $invoker, Swift_Events_EventDispatcher $eventDispatcher)
{
$this->_invoker = $invoker;
$this->_eventDispatcher = $eventDispatcher;
}
/**
* Not used.
*/
public function isStarted()
{
return false;
}
/**
* Not used.
*/
public function start()
{
}
/**
* Not used.
*/
public function stop()
{
}
/**
* Set the additional parameters used on the mail() function.
*
* This string is formatted for sprintf() where %s is the sender address.
*
* @param string $params
*
* @return Swift_Transport_MailTransport
*/
public function setExtraParams($params)
{
$this->_extraParams = $params;
return $this;
}
/**
* Get the additional parameters used on the mail() function.
*
* This string is formatted for sprintf() where %s is the sender address.
*
* @return string
*/
public function getExtraParams()
{
return $this->_extraParams;
}
/**
* Send the given Message.
*
* Recipient/sender data will be retrieved from the Message API.
* The return value is the number of recipients who were accepted for delivery.
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
$failedRecipients = (array) $failedRecipients;
if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) {
$this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
if ($evt->bubbleCancelled()) {
return 0;
}
}
$count = (
count((array) $message->getTo())
+ count((array) $message->getCc())
+ count((array) $message->getBcc())
);
$toHeader = $message->getHeaders()->get('To');
$subjectHeader = $message->getHeaders()->get('Subject');
if (!$toHeader) {
$this->_throwException(new Swift_TransportException('Cannot send message without a recipient'));
}
$to = $toHeader->getFieldBody();
$subject = $subjectHeader ? $subjectHeader->getFieldBody() : '';
$reversePath = $this->_getReversePath($message);
// Remove headers that would otherwise be duplicated
$message->getHeaders()->remove('To');
$message->getHeaders()->remove('Subject');
$messageStr = $message->toString();
$message->getHeaders()->set($toHeader);
$message->getHeaders()->set($subjectHeader);
// Separate headers from body
if (false !== $endHeaders = strpos($messageStr, "\r\n\r\n")) {
$headers = substr($messageStr, 0, $endHeaders)."\r\n"; //Keep last EOL
$body = substr($messageStr, $endHeaders + 4);
} else {
$headers = $messageStr."\r\n";
$body = '';
}
unset($messageStr);
if ("\r\n" != PHP_EOL) {
// Non-windows (not using SMTP)
$headers = str_replace("\r\n", PHP_EOL, $headers);
$subject = str_replace("\r\n", PHP_EOL, $subject);
$body = str_replace("\r\n", PHP_EOL, $body);
} else {
// Windows, using SMTP
$headers = str_replace("\r\n.", "\r\n..", $headers);
$subject = str_replace("\r\n.", "\r\n..", $subject);
$body = str_replace("\r\n.", "\r\n..", $body);
}
if ($this->_invoker->mail($to, $subject, $body, $headers,
sprintf($this->_extraParams, escapeshellarg($reversePath)))) {
if ($evt) {
$evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
$evt->setFailedRecipients($failedRecipients);
$this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
}
} else {
$failedRecipients = array_merge(
$failedRecipients,
array_keys((array) $message->getTo()),
array_keys((array) $message->getCc()),
array_keys((array) $message->getBcc())
);
if ($evt) {
$evt->setResult(Swift_Events_SendEvent::RESULT_FAILED);
$evt->setFailedRecipients($failedRecipients);
$this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
}
$message->generateId();
$count = 0;
}
return $count;
}
/**
* Register a plugin.
*
* @param Swift_Events_EventListener $plugin
*/
public function registerPlugin(Swift_Events_EventListener $plugin)
{
$this->_eventDispatcher->bindEventListener($plugin);
}
/** Throw a TransportException, first sending it to any listeners */
protected function _throwException(Swift_TransportException $e)
{
if ($evt = $this->_eventDispatcher->createTransportExceptionEvent($this, $e)) {
$this->_eventDispatcher->dispatchEvent($evt, 'exceptionThrown');
if (!$evt->bubbleCancelled()) {
throw $e;
}
} else {
throw $e;
}
}
/** Determine the best-use reverse path for this message */
private function _getReversePath(Swift_Mime_Message $message)
{
$return = $message->getReturnPath();
$sender = $message->getSender();
$from = $message->getFrom();
$path = null;
if (!empty($return)) {
$path = $return;
} elseif (!empty($sender)) {
$keys = array_keys($sender);
$path = array_shift($keys);
} elseif (!empty($from)) {
$keys = array_keys($from);
$path = array_shift($keys);
}
return $path;
}
}

View File

@@ -0,0 +1,93 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2009 Fabien Potencier <fabien.potencier@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Pretends messages have been sent, but just ignores them.
*
* @author Fabien Potencier
*/
class Swift_Transport_NullTransport implements Swift_Transport
{
/** The event dispatcher from the plugin API */
private $_eventDispatcher;
/**
* Constructor.
*/
public function __construct(Swift_Events_EventDispatcher $eventDispatcher)
{
$this->_eventDispatcher = $eventDispatcher;
}
/**
* Tests if this Transport mechanism has started.
*
* @return bool
*/
public function isStarted()
{
return true;
}
/**
* Starts this Transport mechanism.
*/
public function start()
{
}
/**
* Stops this Transport mechanism.
*/
public function stop()
{
}
/**
* Sends the given message.
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int The number of sent emails
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) {
$this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
if ($evt->bubbleCancelled()) {
return 0;
}
}
if ($evt) {
$evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
$this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
}
$count = (
count((array) $message->getTo())
+ count((array) $message->getCc())
+ count((array) $message->getBcc())
);
return $count;
}
/**
* Register a plugin.
*
* @param Swift_Events_EventListener $plugin
*/
public function registerPlugin(Swift_Events_EventListener $plugin)
{
$this->_eventDispatcher->bindEventListener($plugin);
}
}

View File

@@ -0,0 +1,160 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* SendmailTransport for sending mail through a Sendmail/Postfix (etc..) binary.
*
* Supported modes are -bs and -t, with any additional flags desired.
* It is advised to use -bs mode since error reporting with -t mode is not
* possible.
*
* @author Chris Corbyn
*/
class Swift_Transport_SendmailTransport extends Swift_Transport_AbstractSmtpTransport
{
/**
* Connection buffer parameters.
*
* @var array
*/
private $_params = array(
'timeout' => 30,
'blocking' => 1,
'command' => '/usr/sbin/sendmail -bs',
'type' => Swift_Transport_IoBuffer::TYPE_PROCESS,
);
/**
* Create a new SendmailTransport with $buf for I/O.
*
* @param Swift_Transport_IoBuffer $buf
* @param Swift_Events_EventDispatcher $dispatcher
*/
public function __construct(Swift_Transport_IoBuffer $buf, Swift_Events_EventDispatcher $dispatcher)
{
parent::__construct($buf, $dispatcher);
}
/**
* Start the standalone SMTP session if running in -bs mode.
*/
public function start()
{
if (false !== strpos($this->getCommand(), ' -bs')) {
parent::start();
}
}
/**
* Set the command to invoke.
*
* If using -t mode you are strongly advised to include -oi or -i in the flags.
* For example: /usr/sbin/sendmail -oi -t
* Swift will append a -f<sender> flag if one is not present.
*
* The recommended mode is "-bs" since it is interactive and failure notifications
* are hence possible.
*
* @param string $command
*
* @return Swift_Transport_SendmailTransport
*/
public function setCommand($command)
{
$this->_params['command'] = $command;
return $this;
}
/**
* Get the sendmail command which will be invoked.
*
* @return string
*/
public function getCommand()
{
return $this->_params['command'];
}
/**
* Send the given Message.
*
* Recipient/sender data will be retrieved from the Message API.
*
* The return value is the number of recipients who were accepted for delivery.
* NOTE: If using 'sendmail -t' you will not be aware of any failures until
* they bounce (i.e. send() will always return 100% success).
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
$failedRecipients = (array) $failedRecipients;
$command = $this->getCommand();
$buffer = $this->getBuffer();
$count = 0;
if (false !== strpos($command, ' -t')) {
if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) {
$this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
if ($evt->bubbleCancelled()) {
return 0;
}
}
if (false === strpos($command, ' -f')) {
$command .= ' -f'.escapeshellarg($this->_getReversePath($message));
}
$buffer->initialize(array_merge($this->_params, array('command' => $command)));
if (false === strpos($command, ' -i') && false === strpos($command, ' -oi')) {
$buffer->setWriteTranslations(array("\r\n" => "\n", "\n." => "\n.."));
} else {
$buffer->setWriteTranslations(array("\r\n" => "\n"));
}
$count = count((array) $message->getTo())
+ count((array) $message->getCc())
+ count((array) $message->getBcc())
;
$message->toByteStream($buffer);
$buffer->flushBuffers();
$buffer->setWriteTranslations(array());
$buffer->terminate();
if ($evt) {
$evt->setResult(Swift_Events_SendEvent::RESULT_SUCCESS);
$evt->setFailedRecipients($failedRecipients);
$this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
}
$message->generateId();
} elseif (false !== strpos($command, ' -bs')) {
$count = parent::send($message, $failedRecipients);
} else {
$this->_throwException(new Swift_TransportException(
'Unsupported sendmail command flags ['.$command.']. '.
'Must be one of "-bs" or "-t" but can include additional flags.'
));
}
return $count;
}
/** Get the params to initialize the buffer */
protected function _getBufferParams()
{
return $this->_params;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* This is the implementation class for {@link Swift_Transport_MailInvoker}.
*
* @author Chris Corbyn
*/
class Swift_Transport_SimpleMailInvoker implements Swift_Transport_MailInvoker
{
/**
* Send mail via the mail() function.
*
* This method takes the same arguments as PHP mail().
*
* @param string $to
* @param string $subject
* @param string $body
* @param string $headers
* @param string $extraParams
*
* @return bool
*/
public function mail($to, $subject, $body, $headers = null, $extraParams = null)
{
if (!ini_get('safe_mode')) {
return @mail($to, $subject, $body, $headers, $extraParams);
}
return @mail($to, $subject, $body, $headers);
}
}

View File

@@ -0,0 +1,36 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Wraps an IoBuffer to send/receive SMTP commands/responses.
*
* @author Chris Corbyn
*/
interface Swift_Transport_SmtpAgent
{
/**
* Get the IoBuffer where read/writes are occurring.
*
* @return Swift_Transport_IoBuffer
*/
public function getBuffer();
/**
* Run a command against the buffer, expecting the given response codes.
*
* If no response codes are given, the response will not be validated.
* If codes are given, an exception will be thrown on an invalid response.
*
* @param string $command
* @param int[] $codes
* @param string[] $failures An array of failures by-reference
*/
public function executeCommand($command, $codes = array(), &$failures = null);
}

View File

@@ -0,0 +1,117 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2009 Fabien Potencier <fabien.potencier@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Stores Messages in a queue.
*
* @author Fabien Potencier
*/
class Swift_Transport_SpoolTransport implements Swift_Transport
{
/** The spool instance */
private $_spool;
/** The event dispatcher from the plugin API */
private $_eventDispatcher;
/**
* Constructor.
*/
public function __construct(Swift_Events_EventDispatcher $eventDispatcher, Swift_Spool $spool = null)
{
$this->_eventDispatcher = $eventDispatcher;
$this->_spool = $spool;
}
/**
* Sets the spool object.
*
* @param Swift_Spool $spool
*
* @return Swift_Transport_SpoolTransport
*/
public function setSpool(Swift_Spool $spool)
{
$this->_spool = $spool;
return $this;
}
/**
* Get the spool object.
*
* @return Swift_Spool
*/
public function getSpool()
{
return $this->_spool;
}
/**
* Tests if this Transport mechanism has started.
*
* @return bool
*/
public function isStarted()
{
return true;
}
/**
* Starts this Transport mechanism.
*/
public function start()
{
}
/**
* Stops this Transport mechanism.
*/
public function stop()
{
}
/**
* Sends the given message.
*
* @param Swift_Mime_Message $message
* @param string[] $failedRecipients An array of failures by-reference
*
* @return int The number of sent e-mail's
*/
public function send(Swift_Mime_Message $message, &$failedRecipients = null)
{
if ($evt = $this->_eventDispatcher->createSendEvent($this, $message)) {
$this->_eventDispatcher->dispatchEvent($evt, 'beforeSendPerformed');
if ($evt->bubbleCancelled()) {
return 0;
}
}
$success = $this->_spool->queueMessage($message);
if ($evt) {
$evt->setResult($success ? Swift_Events_SendEvent::RESULT_SPOOLED : Swift_Events_SendEvent::RESULT_FAILED);
$this->_eventDispatcher->dispatchEvent($evt, 'sendPerformed');
}
return 1;
}
/**
* Register a plugin.
*
* @param Swift_Events_EventListener $plugin
*/
public function registerPlugin(Swift_Events_EventListener $plugin)
{
$this->_eventDispatcher->bindEventListener($plugin);
}
}

View File

@@ -0,0 +1,325 @@
<?php
/*
* This file is part of SwiftMailer.
* (c) 2004-2009 Chris Corbyn
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* A generic IoBuffer implementation supporting remote sockets and local processes.
*
* @author Chris Corbyn
*/
class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_Transport_IoBuffer
{
/** A primary socket */
private $_stream;
/** The input stream */
private $_in;
/** The output stream */
private $_out;
/** Buffer initialization parameters */
private $_params = array();
/** The ReplacementFilterFactory */
private $_replacementFactory;
/** Translations performed on data being streamed into the buffer */
private $_translations = array();
/**
* Create a new StreamBuffer using $replacementFactory for transformations.
*
* @param Swift_ReplacementFilterFactory $replacementFactory
*/
public function __construct(Swift_ReplacementFilterFactory $replacementFactory)
{
$this->_replacementFactory = $replacementFactory;
}
/**
* Perform any initialization needed, using the given $params.
*
* Parameters will vary depending upon the type of IoBuffer used.
*
* @param array $params
*/
public function initialize(array $params)
{
$this->_params = $params;
switch ($params['type']) {
case self::TYPE_PROCESS:
$this->_establishProcessConnection();
break;
case self::TYPE_SOCKET:
default:
$this->_establishSocketConnection();
break;
}
}
/**
* Set an individual param on the buffer (e.g. switching to SSL).
*
* @param string $param
* @param mixed $value
*/
public function setParam($param, $value)
{
if (isset($this->_stream)) {
switch ($param) {
case 'timeout':
if ($this->_stream) {
stream_set_timeout($this->_stream, $value);
}
break;
case 'blocking':
if ($this->_stream) {
stream_set_blocking($this->_stream, 1);
}
}
}
$this->_params[$param] = $value;
}
public function startTLS()
{
return stream_socket_enable_crypto($this->_stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
}
/**
* Perform any shutdown logic needed.
*/
public function terminate()
{
if (isset($this->_stream)) {
switch ($this->_params['type']) {
case self::TYPE_PROCESS:
fclose($this->_in);
fclose($this->_out);
proc_close($this->_stream);
break;
case self::TYPE_SOCKET:
default:
fclose($this->_stream);
break;
}
}
$this->_stream = null;
$this->_out = null;
$this->_in = null;
}
/**
* Set an array of string replacements which should be made on data written
* to the buffer.
*
* This could replace LF with CRLF for example.
*
* @param string[] $replacements
*/
public function setWriteTranslations(array $replacements)
{
foreach ($this->_translations as $search => $replace) {
if (!isset($replacements[$search])) {
$this->removeFilter($search);
unset($this->_translations[$search]);
}
}
foreach ($replacements as $search => $replace) {
if (!isset($this->_translations[$search])) {
$this->addFilter(
$this->_replacementFactory->createFilter($search, $replace), $search
);
$this->_translations[$search] = true;
}
}
}
/**
* Get a line of output (including any CRLF).
*
* The $sequence number comes from any writes and may or may not be used
* depending upon the implementation.
*
* @param int $sequence of last write to scan from
*
* @throws Swift_IoException
*
* @return string
*/
public function readLine($sequence)
{
if (isset($this->_out) && !feof($this->_out)) {
$line = fgets($this->_out);
if (strlen($line) == 0) {
$metas = stream_get_meta_data($this->_out);
if ($metas['timed_out']) {
throw new Swift_IoException(
'Connection to '.
$this->_getReadConnectionDescription().
' Timed Out'
);
}
}
return $line;
}
}
/**
* Reads $length bytes from the stream into a string and moves the pointer
* through the stream by $length.
*
* If less bytes exist than are requested the remaining bytes are given instead.
* If no bytes are remaining at all, boolean false is returned.
*
* @param int $length
*
* @throws Swift_IoException
*
* @return string|bool
*/
public function read($length)
{
if (isset($this->_out) && !feof($this->_out)) {
$ret = fread($this->_out, $length);
if (strlen($ret) == 0) {
$metas = stream_get_meta_data($this->_out);
if ($metas['timed_out']) {
throw new Swift_IoException(
'Connection to '.
$this->_getReadConnectionDescription().
' Timed Out'
);
}
}
return $ret;
}
}
/** Not implemented */
public function setReadPointer($byteOffset)
{
}
/** Flush the stream contents */
protected function _flush()
{
if (isset($this->_in)) {
fflush($this->_in);
}
}
/** Write this bytes to the stream */
protected function _commit($bytes)
{
if (isset($this->_in)) {
$bytesToWrite = strlen($bytes);
$totalBytesWritten = 0;
while ($totalBytesWritten < $bytesToWrite) {
$bytesWritten = fwrite($this->_in, substr($bytes, $totalBytesWritten));
if (false === $bytesWritten || 0 === $bytesWritten) {
break;
}
$totalBytesWritten += $bytesWritten;
}
if ($totalBytesWritten > 0) {
return ++$this->_sequence;
}
}
}
/**
* Establishes a connection to a remote server.
*/
private function _establishSocketConnection()
{
$host = $this->_params['host'];
if (!empty($this->_params['protocol'])) {
$host = $this->_params['protocol'].'://'.$host;
}
$timeout = 15;
if (!empty($this->_params['timeout'])) {
$timeout = $this->_params['timeout'];
}
$options = array();
if (!empty($this->_params['sourceIp'])) {
$options['socket']['bindto'] = $this->_params['sourceIp'].':0';
}
if (isset($this->_params['stream_context_options'])) {
$options = array_merge($options, $this->_params['stream_context_options']);
}
$streamContext = stream_context_create($options);
$this->_stream = @stream_socket_client($host.':'.$this->_params['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $streamContext);
if (false === $this->_stream) {
throw new Swift_TransportException(
'Connection could not be established with host '.$this->_params['host'].
' ['.$errstr.' #'.$errno.']'
);
}
if (!empty($this->_params['blocking'])) {
stream_set_blocking($this->_stream, 1);
} else {
stream_set_blocking($this->_stream, 0);
}
stream_set_timeout($this->_stream, $timeout);
$this->_in = &$this->_stream;
$this->_out = &$this->_stream;
}
/**
* Opens a process for input/output.
*/
private function _establishProcessConnection()
{
$command = $this->_params['command'];
$descriptorSpec = array(
0 => array('pipe', 'r'),
1 => array('pipe', 'w'),
2 => array('pipe', 'w'),
);
$this->_stream = proc_open($command, $descriptorSpec, $pipes);
stream_set_blocking($pipes[2], 0);
if ($err = stream_get_contents($pipes[2])) {
throw new Swift_TransportException(
'Process could not be started ['.$err.']'
);
}
$this->_in = &$pipes[0];
$this->_out = &$pipes[1];
}
private function _getReadConnectionDescription()
{
switch ($this->_params['type']) {
case self::TYPE_PROCESS:
return 'Process '.$this->_params['command'];
break;
case self::TYPE_SOCKET:
default:
$host = $this->_params['host'];
if (!empty($this->_params['protocol'])) {
$host = $this->_params['protocol'].'://'.$host;
}
$host .= ':'.$this->_params['port'];
return $host;
break;
}
}
}