Version 4.1.19
This commit is contained in:
1 parent
28bd025b35
commit
2bd5025018
2674 files changed
+233518
-77766
No files matched your search
+130
-60
@@ -2,9 +2,9 @@
|
||||
/**
|
||||
* @brief SMTP Email Class
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @package IPS Community Suite
|
||||
* @since 17 Apr 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -24,41 +24,60 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
class _SMTP extends \IPS\Email
|
||||
{
|
||||
/**
|
||||
* @brief SMTP Conversation Log
|
||||
* @brief SMTP Protocol ("tls", "ssl" or "plain")
|
||||
*/
|
||||
public $log;
|
||||
protected $smtpProtocol;
|
||||
|
||||
/**
|
||||
* @brief SMTP Host
|
||||
*/
|
||||
protected $smtpHost;
|
||||
|
||||
/**
|
||||
* @brief SMTP Port
|
||||
*/
|
||||
protected $smtpPort;
|
||||
|
||||
/**
|
||||
* @brief SMTP Username
|
||||
*/
|
||||
protected $smtpUser;
|
||||
|
||||
/**
|
||||
* @brief SMTP Password
|
||||
*/
|
||||
protected $smtpPass;
|
||||
|
||||
/**
|
||||
* @brief SMTP Connections
|
||||
*/
|
||||
protected static $smtp = array();
|
||||
|
||||
/**
|
||||
* @brief Connection Key
|
||||
*/
|
||||
protected $connectionKey;
|
||||
|
||||
/**
|
||||
* @brief Log
|
||||
*/
|
||||
protected $log = '';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
public function __construct( $smtpProtocol, $smtpHost, $smtpPort, $smtpUser, $smtpPass )
|
||||
{
|
||||
$this->log = '';
|
||||
$this->smtpProtocol = $smtpProtocol;
|
||||
$this->smtpHost = $smtpHost;
|
||||
$this->smtpPort = $smtpPort;
|
||||
$this->smtpUser = $smtpUser;
|
||||
$this->smtpPass = $smtpPass;
|
||||
$this->connectionKey = md5( $smtpProtocol . $smtpHost . $smtpPort . $smtpUser . $smtpPass );
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if( static::$smtp !== NULL )
|
||||
{
|
||||
$this->_sendCommand( 'quit' );
|
||||
|
||||
@fclose( static::$smtp );
|
||||
static::$smtp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SMTP Connection
|
||||
*/
|
||||
protected static $smtp = NULL;
|
||||
|
||||
/**
|
||||
* Connect to server
|
||||
*
|
||||
@@ -67,25 +86,27 @@ class _SMTP extends \IPS\Email
|
||||
public function connect()
|
||||
{
|
||||
/* Do we already have a connection? */
|
||||
if( static::$smtp !== NULL )
|
||||
if( array_key_exists( $this->connectionKey, static::$smtp ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Connect */
|
||||
static::$smtp = @fsockopen( ( ( \IPS\Settings::i()->smtp_protocol == 'ssl' ) ? 'ssl://' : '' ) . \IPS\Settings::i()->smtp_host, \IPS\Settings::i()->smtp_port, $errno, $errstr );
|
||||
|
||||
if ( !static::$smtp )
|
||||
$connection = @fsockopen( ( ( $this->smtpProtocol == 'ssl' ) ? 'ssl://' : '' ) . $this->smtpHost, $this->smtpPort, $errno, $errstr );
|
||||
if ( !$connection )
|
||||
{
|
||||
static::$smtp = NULL;
|
||||
throw new \IPS\Email\Outgoing\Exception( $errstr, $errno );
|
||||
}
|
||||
static::$smtp[ $this->connectionKey ] = $connection;
|
||||
register_shutdown_function(function( $object ){
|
||||
$object->_sendCommand( 'quit' );
|
||||
@fclose( static::$smtp[ $object->connectionKey ] );
|
||||
unset( static::$smtp[ $object->connectionKey ] );
|
||||
}, $this );
|
||||
|
||||
/* Check the initial response is okay */
|
||||
$announce = $this->_getResponse();
|
||||
$this->log .= $announce;
|
||||
$responseCode = mb_substr( $announce, 0, 3 );
|
||||
|
||||
if ( $responseCode != 220 )
|
||||
{
|
||||
throw new \IPS\Email\Outgoing\Exception( \IPS\Member::loggedIn()->language()->addToStack('smtpmail_fsock_error_initial', FALSE, array( 'sprintf' => array( $responseCode ) ) ) );
|
||||
@@ -95,79 +116,124 @@ class _SMTP extends \IPS\Email
|
||||
try
|
||||
{
|
||||
$helo = 'EHLO';
|
||||
$responseCode = $this->_sendCommand( 'EHLO ' . \IPS\Settings::i()->smtp_host, 250 );
|
||||
$responseCode = $this->_sendCommand( 'EHLO ' . $this->smtpHost, 250 );
|
||||
}
|
||||
catch ( \IPS\Email\Outgoing\Exception $e )
|
||||
{
|
||||
$helo = 'HELO';
|
||||
$responseCode = $this->_sendCommand( 'HELO ' . \IPS\Settings::i()->smtp_host, 250 );
|
||||
$responseCode = $this->_sendCommand( 'HELO ' . $this->smtpHost, 250 );
|
||||
}
|
||||
|
||||
/* Is TLS being used? */
|
||||
if( \IPS\Settings::i()->smtp_protocol == 'tls' )
|
||||
if( $this->smtpProtocol == 'tls' )
|
||||
{
|
||||
$this->_sendCommand( 'STARTTLS', 220 );
|
||||
|
||||
if ( !@\stream_socket_enable_crypto( static::$smtp, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT ) )
|
||||
if ( !@\stream_socket_enable_crypto( static::$smtp[ $this->connectionKey ], TRUE, version_compare( PHP_VERSION, '5.6.7' ) >= 0 ? STREAM_CRYPTO_METHOD_SSLv23_CLIENT : STREAM_CRYPTO_METHOD_TLS_CLIENT ) )
|
||||
{
|
||||
throw new \IPS\Email\Outgoing\Exception( \IPS\Member::loggedIn()->language()->addToStack('smtpmail_tls_failed') );
|
||||
}
|
||||
|
||||
/* Exchange server (at least) wants EHLO resending for STARTTLS */
|
||||
$this->_sendCommand( $helo . ' ' . \IPS\Settings::i()->smtp_host, 250 );
|
||||
$this->_sendCommand( $helo . ' ' . $this->smtpHost, 250 );
|
||||
}
|
||||
|
||||
/* Authenticate */
|
||||
if ( \IPS\Settings::i()->smtp_user )
|
||||
if ( $this->smtpUser )
|
||||
{
|
||||
$responseCode = $this->_sendCommand( 'AUTH LOGIN', 334 );
|
||||
$responseCode = $this->_sendCommand( base64_encode( \IPS\Settings::i()->smtp_user ), 334 );
|
||||
$responseCode = $this->_sendCommand( base64_encode( \IPS\Settings::i()->smtp_pass ), 235 );
|
||||
$responseCode = $this->_sendCommand( base64_encode( $this->smtpUser ), 334 );
|
||||
$responseCode = $this->_sendCommand( base64_encode( $this->smtpPass ), 235 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send
|
||||
*
|
||||
* @throws \Exception
|
||||
* @note We use $this->from here instead of the 'From' header, because the header may have a display name embedded
|
||||
* Send the email
|
||||
*
|
||||
* @param mixed $to The member or email address, or array of members or email addresses, to send to
|
||||
* @param mixed $cc Addresses to CC (can also be email, member or array of either)
|
||||
* @param mixed $bcc Addresses to BCC (can also be email, member or array of either)
|
||||
* @param mixed $fromEmail The email address to send from. If NULL, default setting is used
|
||||
* @param mixed $fromName The name the email should appear from. If NULL, default setting is used
|
||||
* @param array $additionalHeaders Additional headers to send
|
||||
* @return void
|
||||
* @throws \IPS\Email\Outgoing\Exception
|
||||
*/
|
||||
protected function _send()
|
||||
public function _send( $to, $cc=array(), $bcc=array(), $fromEmail = NULL, $fromName = NULL, $additionalHeaders = array() )
|
||||
{
|
||||
/* Get the from email */
|
||||
$fromEmail = $fromEmail ?: \IPS\Settings::i()->email_out;
|
||||
|
||||
/* SMTP requires you to do CC/BCC by sending a RCPT TO command for each recipient. We'll hide BCC by not actually setting that header */
|
||||
$recipieintsForSmtp = explode( ',', static::_parseRecipients( $to, TRUE ) );
|
||||
if ( $cc )
|
||||
{
|
||||
$recipieintsForSmtp = array_merge( $recipieintsForSmtp, explode( ',', static::_parseRecipients( $cc, TRUE ) ) );
|
||||
}
|
||||
if ( $bcc )
|
||||
{
|
||||
$recipieintsForSmtp = array_merge( $recipieintsForSmtp, explode( ',', static::_parseRecipients( $bcc, TRUE ) ) );
|
||||
}
|
||||
$recipieintsForSmtp = array_unique( array_map( 'trim', $recipieintsForSmtp ) );
|
||||
|
||||
/* Send */
|
||||
$this->_sendCompiled( $fromEmail, $recipieintsForSmtp, $this->compileFullEmail( $to, $cc, array(), $fromEmail, $fromName, $additionalHeaders ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an email
|
||||
*
|
||||
* @param string $fromEmail The email address to send from
|
||||
* @param array $toEmails Array of email addresses to send to
|
||||
* @param string $email The full email (with headers, etc.) except the Bcc header
|
||||
* @param array $additionalHeaders Additional headers to send
|
||||
* @return void
|
||||
* @throws \IPS\Email\Outgoing\Exception
|
||||
*/
|
||||
public function _sendCompiled( $fromEmail, $toEmails, $email )
|
||||
{
|
||||
$this->connect();
|
||||
$this->_sendCommand( "MAIL FROM:<{$this->from}>", 250 );
|
||||
|
||||
foreach ( $this->to as $to )
|
||||
$this->_sendCommand( "MAIL FROM:<{$fromEmail}>", 250 );
|
||||
|
||||
foreach ( $toEmails as $toEmail )
|
||||
{
|
||||
$this->_sendCommand( "RCPT TO:<{$to}>", 250 );
|
||||
$this->_sendCommand( "RCPT TO:<{$toEmail}>", 250 );
|
||||
}
|
||||
|
||||
$this->_sendCommand( 'DATA', 354 );
|
||||
$this->_sendCommand( "{$this->_compileHeaders()}\r\n\r\n{$this->message}\r\n.", 250 );
|
||||
$this->_sendCommand( $email . "\r\n.", 250 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send SMTP Command
|
||||
*
|
||||
* @param string $command The command
|
||||
* @param int $expectedResponse The expected response code. Will throw an exception if different.
|
||||
* @param string $command The command
|
||||
* @param int|NULL $expectedResponse The expected response code. Will throw an exception if different.
|
||||
* @param bool $resetOnFailure If the command fails, issue a RSET (reset) command
|
||||
* @return string Response
|
||||
* @throws \IPS\Email\Outgoing\Exception
|
||||
*/
|
||||
protected function _sendCommand( $command, $expectedResponse=NULL )
|
||||
protected function _sendCommand( $command, $expectedResponse=NULL, $resetOnFailure=FALSE )
|
||||
{
|
||||
/* Send */
|
||||
/* Log */
|
||||
$this->log .= "> {$command}\r\n";
|
||||
fputs( static::$smtp, $command . "\r\n" );
|
||||
|
||||
/* Send */
|
||||
fputs( static::$smtp[ $this->connectionKey ], $command . "\r\n" );
|
||||
|
||||
/* Read */
|
||||
$response = $this->_getResponse();
|
||||
$this->log .= $response;
|
||||
|
||||
/* Get response code */
|
||||
$code = intval( mb_substr( $response, 0, 3 ) );
|
||||
if ( $expectedResponse !== NULL and $code !== $expectedResponse )
|
||||
{
|
||||
if( $resetOnFailure === TRUE )
|
||||
{
|
||||
$this->_sendCommand( 'RSET' );
|
||||
}
|
||||
|
||||
throw new \IPS\Email\Outgoing\Exception( $response, $code );
|
||||
}
|
||||
|
||||
@@ -184,7 +250,7 @@ class _SMTP extends \IPS\Email
|
||||
{
|
||||
/* Read */
|
||||
$response = '';
|
||||
while ( $line = @fgets( static::$smtp, 515 ) )
|
||||
while ( $line = @fgets( static::$smtp[ $this->connectionKey ], 515 ) )
|
||||
{
|
||||
$response .= $line;
|
||||
if ( mb_substr($line, 3, 1) == " " )
|
||||
@@ -192,10 +258,14 @@ class _SMTP extends \IPS\Email
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Log */
|
||||
$this->log .= $response;
|
||||
|
||||
/* Return */
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the SMTP log
|
||||
*
|
||||
|
||||
Reference in new issue
Block a user