Update PHPMailer
							parent
							
								
									94e8b2e6af
								
							
						
					
					
						commit
						406cdcce31
					
				|  | @ -0,0 +1,247 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * PHPMailer - PHP email creation and transport class. | ||||||
|  |  * PHP Version 5.5. | ||||||
|  |  * | ||||||
|  |  * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project | ||||||
|  |  * | ||||||
|  |  * @author    Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> | ||||||
|  |  * @author    Jim Jagielski (jimjag) <jimjag@gmail.com> | ||||||
|  |  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> | ||||||
|  |  * @author    Brent R. Matzelle (original founder) | ||||||
|  |  * @copyright 2012 - 2023 Marcus Bointon | ||||||
|  |  * @copyright 2010 - 2012 Jim Jagielski | ||||||
|  |  * @copyright 2004 - 2009 Andy Prevost | ||||||
|  |  * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License | ||||||
|  |  * @note      This program is distributed in the hope that it will be useful - WITHOUT | ||||||
|  |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace PHPMailer\PHPMailer; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Configure PHPMailer with DSN string. | ||||||
|  |  * | ||||||
|  |  * @see https://en.wikipedia.org/wiki/Data_source_name | ||||||
|  |  * | ||||||
|  |  * @author Oleg Voronkovich <oleg-voronkovich@yandex.ru> | ||||||
|  |  */ | ||||||
|  | class DSNConfigurator | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Create new PHPMailer instance configured by DSN. | ||||||
|  |      * | ||||||
|  |      * @param string $dsn        DSN | ||||||
|  |      * @param bool   $exceptions Should we throw external exceptions? | ||||||
|  |      * | ||||||
|  |      * @return PHPMailer | ||||||
|  |      */ | ||||||
|  |     public static function mailer($dsn, $exceptions = null) | ||||||
|  |     { | ||||||
|  |         static $configurator = null; | ||||||
|  | 
 | ||||||
|  |         if (null === $configurator) { | ||||||
|  |             $configurator = new DSNConfigurator(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $configurator->configure(new PHPMailer($exceptions), $dsn); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Configure PHPMailer instance with DSN string. | ||||||
|  |      * | ||||||
|  |      * @param PHPMailer $mailer PHPMailer instance | ||||||
|  |      * @param string    $dsn    DSN | ||||||
|  |      * | ||||||
|  |      * @return PHPMailer | ||||||
|  |      */ | ||||||
|  |     public function configure(PHPMailer $mailer, $dsn) | ||||||
|  |     { | ||||||
|  |         $config = $this->parseDSN($dsn); | ||||||
|  | 
 | ||||||
|  |         $this->applyConfig($mailer, $config); | ||||||
|  | 
 | ||||||
|  |         return $mailer; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Parse DSN string. | ||||||
|  |      * | ||||||
|  |      * @param string $dsn DSN | ||||||
|  |      * | ||||||
|  |      * @throws Exception If DSN is malformed | ||||||
|  |      * | ||||||
|  |      * @return array Configuration | ||||||
|  |      */ | ||||||
|  |     private function parseDSN($dsn) | ||||||
|  |     { | ||||||
|  |         $config = $this->parseUrl($dsn); | ||||||
|  | 
 | ||||||
|  |         if (false === $config || !isset($config['scheme']) || !isset($config['host'])) { | ||||||
|  |             throw new Exception( | ||||||
|  |                 sprintf('Malformed DSN: "%s".', $dsn) | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (isset($config['query'])) { | ||||||
|  |             parse_str($config['query'], $config['query']); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $config; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Apply configuration to mailer. | ||||||
|  |      * | ||||||
|  |      * @param PHPMailer $mailer PHPMailer instance | ||||||
|  |      * @param array     $config Configuration | ||||||
|  |      * | ||||||
|  |      * @throws Exception If scheme is invalid | ||||||
|  |      */ | ||||||
|  |     private function applyConfig(PHPMailer $mailer, $config) | ||||||
|  |     { | ||||||
|  |         switch ($config['scheme']) { | ||||||
|  |             case 'mail': | ||||||
|  |                 $mailer->isMail(); | ||||||
|  |                 break; | ||||||
|  |             case 'sendmail': | ||||||
|  |                 $mailer->isSendmail(); | ||||||
|  |                 break; | ||||||
|  |             case 'qmail': | ||||||
|  |                 $mailer->isQmail(); | ||||||
|  |                 break; | ||||||
|  |             case 'smtp': | ||||||
|  |             case 'smtps': | ||||||
|  |                 $mailer->isSMTP(); | ||||||
|  |                 $this->configureSMTP($mailer, $config); | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 throw new Exception( | ||||||
|  |                     sprintf( | ||||||
|  |                         'Invalid scheme: "%s". Allowed values: "mail", "sendmail", "qmail", "smtp", "smtps".', | ||||||
|  |                         $config['scheme'] | ||||||
|  |                     ) | ||||||
|  |                 ); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (isset($config['query'])) { | ||||||
|  |             $this->configureOptions($mailer, $config['query']); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Configure SMTP. | ||||||
|  |      * | ||||||
|  |      * @param PHPMailer $mailer PHPMailer instance | ||||||
|  |      * @param array     $config Configuration | ||||||
|  |      */ | ||||||
|  |     private function configureSMTP($mailer, $config) | ||||||
|  |     { | ||||||
|  |         $isSMTPS = 'smtps' === $config['scheme']; | ||||||
|  | 
 | ||||||
|  |         if ($isSMTPS) { | ||||||
|  |             $mailer->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $mailer->Host = $config['host']; | ||||||
|  | 
 | ||||||
|  |         if (isset($config['port'])) { | ||||||
|  |             $mailer->Port = $config['port']; | ||||||
|  |         } elseif ($isSMTPS) { | ||||||
|  |             $mailer->Port = SMTP::DEFAULT_SECURE_PORT; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $mailer->SMTPAuth = isset($config['user']) || isset($config['pass']); | ||||||
|  | 
 | ||||||
|  |         if (isset($config['user'])) { | ||||||
|  |             $mailer->Username = $config['user']; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (isset($config['pass'])) { | ||||||
|  |             $mailer->Password = $config['pass']; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Configure options. | ||||||
|  |      * | ||||||
|  |      * @param PHPMailer $mailer  PHPMailer instance | ||||||
|  |      * @param array     $options Options | ||||||
|  |      * | ||||||
|  |      * @throws Exception If option is unknown | ||||||
|  |      */ | ||||||
|  |     private function configureOptions(PHPMailer $mailer, $options) | ||||||
|  |     { | ||||||
|  |         $allowedOptions = get_object_vars($mailer); | ||||||
|  | 
 | ||||||
|  |         unset($allowedOptions['Mailer']); | ||||||
|  |         unset($allowedOptions['SMTPAuth']); | ||||||
|  |         unset($allowedOptions['Username']); | ||||||
|  |         unset($allowedOptions['Password']); | ||||||
|  |         unset($allowedOptions['Hostname']); | ||||||
|  |         unset($allowedOptions['Port']); | ||||||
|  |         unset($allowedOptions['ErrorInfo']); | ||||||
|  | 
 | ||||||
|  |         $allowedOptions = \array_keys($allowedOptions); | ||||||
|  | 
 | ||||||
|  |         foreach ($options as $key => $value) { | ||||||
|  |             if (!in_array($key, $allowedOptions)) { | ||||||
|  |                 throw new Exception( | ||||||
|  |                     sprintf( | ||||||
|  |                         'Unknown option: "%s". Allowed values: "%s"', | ||||||
|  |                         $key, | ||||||
|  |                         implode('", "', $allowedOptions) | ||||||
|  |                     ) | ||||||
|  |                 ); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             switch ($key) { | ||||||
|  |                 case 'AllowEmpty': | ||||||
|  |                 case 'SMTPAutoTLS': | ||||||
|  |                 case 'SMTPKeepAlive': | ||||||
|  |                 case 'SingleTo': | ||||||
|  |                 case 'UseSendmailOptions': | ||||||
|  |                 case 'do_verp': | ||||||
|  |                 case 'DKIM_copyHeaderFields': | ||||||
|  |                     $mailer->$key = (bool) $value; | ||||||
|  |                     break; | ||||||
|  |                 case 'Priority': | ||||||
|  |                 case 'SMTPDebug': | ||||||
|  |                 case 'WordWrap': | ||||||
|  |                     $mailer->$key = (int) $value; | ||||||
|  |                     break; | ||||||
|  |                 default: | ||||||
|  |                     $mailer->$key = $value; | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Parse a URL. | ||||||
|  |      * Wrapper for the built-in parse_url function to work around a bug in PHP 5.5. | ||||||
|  |      * | ||||||
|  |      * @param string $url URL | ||||||
|  |      * | ||||||
|  |      * @return array|false | ||||||
|  |      */ | ||||||
|  |     protected function parseUrl($url) | ||||||
|  |     { | ||||||
|  |         if (\PHP_VERSION_ID >= 50600 || false === strpos($url, '?')) { | ||||||
|  |             return parse_url($url); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $chunks = explode('?', $url); | ||||||
|  |         if (is_array($chunks)) { | ||||||
|  |             $result = parse_url($chunks[0]); | ||||||
|  |             if (is_array($result)) { | ||||||
|  |                 $result['query'] = $chunks[1]; | ||||||
|  |             } | ||||||
|  |             return $result; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| <?php | <?php | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * PHPMailer Exception class. |  * PHPMailer Exception class. | ||||||
|  * PHP Version 5.5. |  * PHP Version 5.5. | ||||||
|  | @ -9,7 +10,7 @@ | ||||||
|  * @author    Jim Jagielski (jimjag) <jimjag@gmail.com> |  * @author    Jim Jagielski (jimjag) <jimjag@gmail.com> | ||||||
|  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> |  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> | ||||||
|  * @author    Brent R. Matzelle (original founder) |  * @author    Brent R. Matzelle (original founder) | ||||||
|  * @copyright 2012 - 2017 Marcus Bointon |  * @copyright 2012 - 2020 Marcus Bointon | ||||||
|  * @copyright 2010 - 2012 Jim Jagielski |  * @copyright 2010 - 2012 Jim Jagielski | ||||||
|  * @copyright 2004 - 2009 Andy Prevost |  * @copyright 2004 - 2009 Andy Prevost | ||||||
|  * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License |  * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License | ||||||
|  | @ -18,6 +19,8 @@ | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE. |  * FITNESS FOR A PARTICULAR PURPOSE. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | namespace PHPMailer\PHPMailer; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * PHPMailer exception handler. |  * PHPMailer exception handler. | ||||||
|  * |  * | ||||||
|  | @ -32,6 +35,6 @@ class Exception extends \Exception | ||||||
|      */ |      */ | ||||||
|     public function errorMessage() |     public function errorMessage() | ||||||
|     { |     { | ||||||
|         return '<strong>' . htmlspecialchars($this->getMessage()) . "</strong><br />\n"; |         return '<strong>' . htmlspecialchars($this->getMessage(), ENT_COMPAT | ENT_HTML401) . "</strong><br />\n"; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| <?php | <?php | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * PHPMailer - PHP email creation and transport class. |  * PHPMailer - PHP email creation and transport class. | ||||||
|  * PHP Version 5.5. |  * PHP Version 5.5. | ||||||
|  | @ -9,7 +10,7 @@ | ||||||
|  * @author    Jim Jagielski (jimjag) <jimjag@gmail.com> |  * @author    Jim Jagielski (jimjag) <jimjag@gmail.com> | ||||||
|  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> |  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> | ||||||
|  * @author    Brent R. Matzelle (original founder) |  * @author    Brent R. Matzelle (original founder) | ||||||
|  * @copyright 2012 - 2015 Marcus Bointon |  * @copyright 2012 - 2020 Marcus Bointon | ||||||
|  * @copyright 2010 - 2012 Jim Jagielski |  * @copyright 2010 - 2012 Jim Jagielski | ||||||
|  * @copyright 2004 - 2009 Andy Prevost |  * @copyright 2004 - 2009 Andy Prevost | ||||||
|  * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License |  * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License | ||||||
|  | @ -18,6 +19,8 @@ | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE. |  * FITNESS FOR A PARTICULAR PURPOSE. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | namespace PHPMailer\PHPMailer; | ||||||
|  | 
 | ||||||
| use League\OAuth2\Client\Grant\RefreshToken; | use League\OAuth2\Client\Grant\RefreshToken; | ||||||
| use League\OAuth2\Client\Provider\AbstractProvider; | use League\OAuth2\Client\Provider\AbstractProvider; | ||||||
| use League\OAuth2\Client\Token\AccessToken; | use League\OAuth2\Client\Token\AccessToken; | ||||||
|  | @ -30,7 +33,7 @@ use League\OAuth2\Client\Token\AccessToken; | ||||||
|  * |  * | ||||||
|  * @author  Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> |  * @author  Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> | ||||||
|  */ |  */ | ||||||
| class OAuth | class OAuth implements OAuthTokenProvider | ||||||
| { | { | ||||||
|     /** |     /** | ||||||
|      * An instance of the League OAuth Client Provider. |      * An instance of the League OAuth Client Provider. | ||||||
|  | @ -120,7 +123,7 @@ class OAuth | ||||||
|      */ |      */ | ||||||
|     public function getOauth64() |     public function getOauth64() | ||||||
|     { |     { | ||||||
|         // Get a new token if it's not available or has expired
 |         //Get a new token if it's not available or has expired
 | ||||||
|         if (null === $this->oauthToken || $this->oauthToken->hasExpired()) { |         if (null === $this->oauthToken || $this->oauthToken->hasExpired()) { | ||||||
|             $this->oauthToken = $this->getToken(); |             $this->oauthToken = $this->getToken(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,44 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * PHPMailer - PHP email creation and transport class. | ||||||
|  |  * PHP Version 5.5. | ||||||
|  |  * | ||||||
|  |  * @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project | ||||||
|  |  * | ||||||
|  |  * @author    Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> | ||||||
|  |  * @author    Jim Jagielski (jimjag) <jimjag@gmail.com> | ||||||
|  |  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> | ||||||
|  |  * @author    Brent R. Matzelle (original founder) | ||||||
|  |  * @copyright 2012 - 2020 Marcus Bointon | ||||||
|  |  * @copyright 2010 - 2012 Jim Jagielski | ||||||
|  |  * @copyright 2004 - 2009 Andy Prevost | ||||||
|  |  * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License | ||||||
|  |  * @note      This program is distributed in the hope that it will be useful - WITHOUT | ||||||
|  |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | namespace PHPMailer\PHPMailer; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * OAuthTokenProvider - OAuth2 token provider interface. | ||||||
|  |  * Provides base64 encoded OAuth2 auth strings for SMTP authentication. | ||||||
|  |  * | ||||||
|  |  * @see     OAuth | ||||||
|  |  * @see     SMTP::authenticate() | ||||||
|  |  * | ||||||
|  |  * @author  Peter Scopes (pdscopes) | ||||||
|  |  * @author  Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk> | ||||||
|  |  */ | ||||||
|  | interface OAuthTokenProvider | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Generate a base64-encoded OAuth token ensuring that the access token has not expired. | ||||||
|  |      * The string to be base 64 encoded should be in the form: | ||||||
|  |      * "user=<user_email_address>\001auth=Bearer <access_token>\001\001" | ||||||
|  |      * | ||||||
|  |      * @return string | ||||||
|  |      */ | ||||||
|  |     public function getOauth64(); | ||||||
|  | } | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -1,4 +1,5 @@ | ||||||
| <?php | <?php | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * PHPMailer POP-Before-SMTP Authentication Class. |  * PHPMailer POP-Before-SMTP Authentication Class. | ||||||
|  * PHP Version 5.5. |  * PHP Version 5.5. | ||||||
|  | @ -9,7 +10,7 @@ | ||||||
|  * @author    Jim Jagielski (jimjag) <jimjag@gmail.com> |  * @author    Jim Jagielski (jimjag) <jimjag@gmail.com> | ||||||
|  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> |  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> | ||||||
|  * @author    Brent R. Matzelle (original founder) |  * @author    Brent R. Matzelle (original founder) | ||||||
|  * @copyright 2012 - 2019 Marcus Bointon |  * @copyright 2012 - 2020 Marcus Bointon | ||||||
|  * @copyright 2010 - 2012 Jim Jagielski |  * @copyright 2010 - 2012 Jim Jagielski | ||||||
|  * @copyright 2004 - 2009 Andy Prevost |  * @copyright 2004 - 2009 Andy Prevost | ||||||
|  * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License |  * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License | ||||||
|  | @ -18,6 +19,8 @@ | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE. |  * FITNESS FOR A PARTICULAR PURPOSE. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | namespace PHPMailer\PHPMailer; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * PHPMailer POP-Before-SMTP Authentication Class. |  * PHPMailer POP-Before-SMTP Authentication Class. | ||||||
|  * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication. |  * Specifically for PHPMailer to use for RFC1939 POP-before-SMTP authentication. | ||||||
|  | @ -43,7 +46,7 @@ class POP3 | ||||||
|      * |      * | ||||||
|      * @var string |      * @var string | ||||||
|      */ |      */ | ||||||
|     const VERSION = '6.1.5'; |     const VERSION = '6.8.0'; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Default POP3 port number. |      * Default POP3 port number. | ||||||
|  | @ -60,12 +63,16 @@ class POP3 | ||||||
|     const DEFAULT_TIMEOUT = 30; |     const DEFAULT_TIMEOUT = 30; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Debug display level. |      * POP3 class debug output mode. | ||||||
|      * Options: 0 = no, 1+ = yes. |      * Debug output level. | ||||||
|  |      * Options: | ||||||
|  |      * @see POP3::DEBUG_OFF: No output | ||||||
|  |      * @see POP3::DEBUG_SERVER: Server messages, connection/server errors | ||||||
|  |      * @see POP3::DEBUG_CLIENT: Client and Server messages, connection/server errors | ||||||
|      * |      * | ||||||
|      * @var int |      * @var int | ||||||
|      */ |      */ | ||||||
|     public $do_debug = 0; |     public $do_debug = self::DEBUG_OFF; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * POP3 mail server hostname. |      * POP3 mail server hostname. | ||||||
|  | @ -128,6 +135,28 @@ class POP3 | ||||||
|      */ |      */ | ||||||
|     const LE = "\r\n"; |     const LE = "\r\n"; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Debug level for no output. | ||||||
|  |      * | ||||||
|  |      * @var int | ||||||
|  |      */ | ||||||
|  |     const DEBUG_OFF = 0; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Debug level to show server -> client messages | ||||||
|  |      * also shows clients connection errors or errors from server | ||||||
|  |      * | ||||||
|  |      * @var int | ||||||
|  |      */ | ||||||
|  |     const DEBUG_SERVER = 1; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Debug level to show client -> server and server -> client messages. | ||||||
|  |      * | ||||||
|  |      * @var int | ||||||
|  |      */ | ||||||
|  |     const DEBUG_CLIENT = 2; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Simple static wrapper for all-in-one POP before SMTP. |      * Simple static wrapper for all-in-one POP before SMTP. | ||||||
|      * |      * | ||||||
|  | @ -170,13 +199,13 @@ class POP3 | ||||||
|     public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0) |     public function authorise($host, $port = false, $timeout = false, $username = '', $password = '', $debug_level = 0) | ||||||
|     { |     { | ||||||
|         $this->host = $host; |         $this->host = $host; | ||||||
|         // If no port value provided, use default
 |         //If no port value provided, use default
 | ||||||
|         if (false === $port) { |         if (false === $port) { | ||||||
|             $this->port = static::DEFAULT_PORT; |             $this->port = static::DEFAULT_PORT; | ||||||
|         } else { |         } else { | ||||||
|             $this->port = (int) $port; |             $this->port = (int) $port; | ||||||
|         } |         } | ||||||
|         // If no timeout value provided, use default
 |         //If no timeout value provided, use default
 | ||||||
|         if (false === $timeout) { |         if (false === $timeout) { | ||||||
|             $this->tval = static::DEFAULT_TIMEOUT; |             $this->tval = static::DEFAULT_TIMEOUT; | ||||||
|         } else { |         } else { | ||||||
|  | @ -185,9 +214,9 @@ class POP3 | ||||||
|         $this->do_debug = $debug_level; |         $this->do_debug = $debug_level; | ||||||
|         $this->username = $username; |         $this->username = $username; | ||||||
|         $this->password = $password; |         $this->password = $password; | ||||||
|         //  Reset the error log
 |         //Reset the error log
 | ||||||
|         $this->errors = []; |         $this->errors = []; | ||||||
|         //  connect
 |         //Connect
 | ||||||
|         $result = $this->connect($this->host, $this->port, $this->tval); |         $result = $this->connect($this->host, $this->port, $this->tval); | ||||||
|         if ($result) { |         if ($result) { | ||||||
|             $login_result = $this->login($this->username, $this->password); |             $login_result = $this->login($this->username, $this->password); | ||||||
|  | @ -197,7 +226,7 @@ class POP3 | ||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         // We need to disconnect regardless of whether the login succeeded
 |         //We need to disconnect regardless of whether the login succeeded
 | ||||||
|         $this->disconnect(); |         $this->disconnect(); | ||||||
| 
 | 
 | ||||||
|         return false; |         return false; | ||||||
|  | @ -214,7 +243,7 @@ class POP3 | ||||||
|      */ |      */ | ||||||
|     public function connect($host, $port = false, $tval = 30) |     public function connect($host, $port = false, $tval = 30) | ||||||
|     { |     { | ||||||
|         //  Are we already connected?
 |         //Are we already connected?
 | ||||||
|         if ($this->connected) { |         if ($this->connected) { | ||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
|  | @ -227,22 +256,22 @@ class POP3 | ||||||
|             $port = static::DEFAULT_PORT; |             $port = static::DEFAULT_PORT; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         //  connect to the POP3 server
 |         //Connect to the POP3 server
 | ||||||
|         $errno = 0; |         $errno = 0; | ||||||
|         $errstr = ''; |         $errstr = ''; | ||||||
|         $this->pop_conn = fsockopen( |         $this->pop_conn = fsockopen( | ||||||
|             $host, //  POP3 Host
 |             $host, //POP3 Host
 | ||||||
|             $port, //  Port #
 |             $port, //Port #
 | ||||||
|             $errno, //  Error Number
 |             $errno, //Error Number
 | ||||||
|             $errstr, //  Error Message
 |             $errstr, //Error Message
 | ||||||
|             $tval |             $tval | ||||||
|         ); //  Timeout (seconds)
 |         ); //Timeout (seconds)
 | ||||||
|         //  Restore the error handler
 |         //Restore the error handler
 | ||||||
|         restore_error_handler(); |         restore_error_handler(); | ||||||
| 
 | 
 | ||||||
|         //  Did we connect?
 |         //Did we connect?
 | ||||||
|         if (false === $this->pop_conn) { |         if (false === $this->pop_conn) { | ||||||
|             //  It would appear not...
 |             //It would appear not...
 | ||||||
|             $this->setError( |             $this->setError( | ||||||
|                 "Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr" |                 "Failed to connect to server $host on port $port. errno: $errno; errstr: $errstr" | ||||||
|             ); |             ); | ||||||
|  | @ -250,14 +279,14 @@ class POP3 | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         //  Increase the stream time-out
 |         //Increase the stream time-out
 | ||||||
|         stream_set_timeout($this->pop_conn, $tval, 0); |         stream_set_timeout($this->pop_conn, $tval, 0); | ||||||
| 
 | 
 | ||||||
|         //  Get the POP3 server response
 |         //Get the POP3 server response
 | ||||||
|         $pop3_response = $this->getResponse(); |         $pop3_response = $this->getResponse(); | ||||||
|         //  Check for the +OK
 |         //Check for the +OK
 | ||||||
|         if ($this->checkResponse($pop3_response)) { |         if ($this->checkResponse($pop3_response)) { | ||||||
|             //  The connection is established and the POP3 server is talking
 |             //The connection is established and the POP3 server is talking
 | ||||||
|             $this->connected = true; |             $this->connected = true; | ||||||
| 
 | 
 | ||||||
|             return true; |             return true; | ||||||
|  | @ -279,6 +308,7 @@ class POP3 | ||||||
|     { |     { | ||||||
|         if (!$this->connected) { |         if (!$this->connected) { | ||||||
|             $this->setError('Not connected to POP3 server'); |             $this->setError('Not connected to POP3 server'); | ||||||
|  |             return false; | ||||||
|         } |         } | ||||||
|         if (empty($username)) { |         if (empty($username)) { | ||||||
|             $username = $this->username; |             $username = $this->username; | ||||||
|  | @ -287,11 +317,11 @@ class POP3 | ||||||
|             $password = $this->password; |             $password = $this->password; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Send the Username
 |         //Send the Username
 | ||||||
|         $this->sendString("USER $username" . static::LE); |         $this->sendString("USER $username" . static::LE); | ||||||
|         $pop3_response = $this->getResponse(); |         $pop3_response = $this->getResponse(); | ||||||
|         if ($this->checkResponse($pop3_response)) { |         if ($this->checkResponse($pop3_response)) { | ||||||
|             // Send the Password
 |             //Send the Password
 | ||||||
|             $this->sendString("PASS $password" . static::LE); |             $this->sendString("PASS $password" . static::LE); | ||||||
|             $pop3_response = $this->getResponse(); |             $pop3_response = $this->getResponse(); | ||||||
|             if ($this->checkResponse($pop3_response)) { |             if ($this->checkResponse($pop3_response)) { | ||||||
|  | @ -307,7 +337,21 @@ class POP3 | ||||||
|      */ |      */ | ||||||
|     public function disconnect() |     public function disconnect() | ||||||
|     { |     { | ||||||
|         $this->sendString('QUIT'); |         // If could not connect at all, no need to disconnect
 | ||||||
|  |         if ($this->pop_conn === false) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $this->sendString('QUIT' . static::LE); | ||||||
|  | 
 | ||||||
|  |         // RFC 1939 shows POP3 server sending a +OK response to the QUIT command.
 | ||||||
|  |         // Try to get it.  Ignore any failures here.
 | ||||||
|  |         try { | ||||||
|  |             $this->getResponse(); | ||||||
|  |         } catch (Exception $e) { | ||||||
|  |             //Do nothing
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         //The QUIT command may cause the daemon to exit, which will kill our connection
 |         //The QUIT command may cause the daemon to exit, which will kill our connection
 | ||||||
|         //So ignore errors here
 |         //So ignore errors here
 | ||||||
|         try { |         try { | ||||||
|  | @ -315,6 +359,10 @@ class POP3 | ||||||
|         } catch (Exception $e) { |         } catch (Exception $e) { | ||||||
|             //Do nothing
 |             //Do nothing
 | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         // Clean up attributes.
 | ||||||
|  |         $this->connected = false; | ||||||
|  |         $this->pop_conn  = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -327,7 +375,7 @@ class POP3 | ||||||
|     protected function getResponse($size = 128) |     protected function getResponse($size = 128) | ||||||
|     { |     { | ||||||
|         $response = fgets($this->pop_conn, $size); |         $response = fgets($this->pop_conn, $size); | ||||||
|         if ($this->do_debug >= 1) { |         if ($this->do_debug >= self::DEBUG_SERVER) { | ||||||
|             echo 'Server -> Client: ', $response; |             echo 'Server -> Client: ', $response; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -344,7 +392,7 @@ class POP3 | ||||||
|     protected function sendString($string) |     protected function sendString($string) | ||||||
|     { |     { | ||||||
|         if ($this->pop_conn) { |         if ($this->pop_conn) { | ||||||
|             if ($this->do_debug >= 2) { //Show client messages when debug >= 2
 |             if ($this->do_debug >= self::DEBUG_CLIENT) { //Show client messages when debug >= 2
 | ||||||
|                 echo 'Client -> Server: ', $string; |                 echo 'Client -> Server: ', $string; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  | @ -382,7 +430,7 @@ class POP3 | ||||||
|     protected function setError($error) |     protected function setError($error) | ||||||
|     { |     { | ||||||
|         $this->errors[] = $error; |         $this->errors[] = $error; | ||||||
|         if ($this->do_debug >= 1) { |         if ($this->do_debug >= self::DEBUG_SERVER) { | ||||||
|             echo '<pre>'; |             echo '<pre>'; | ||||||
|             foreach ($this->errors as $e) { |             foreach ($this->errors as $e) { | ||||||
|                 print_r($e); |                 print_r($e); | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| <?php | <?php | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * PHPMailer RFC821 SMTP email transport class. |  * PHPMailer RFC821 SMTP email transport class. | ||||||
|  * PHP Version 5.5. |  * PHP Version 5.5. | ||||||
|  | @ -9,7 +10,7 @@ | ||||||
|  * @author    Jim Jagielski (jimjag) <jimjag@gmail.com> |  * @author    Jim Jagielski (jimjag) <jimjag@gmail.com> | ||||||
|  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> |  * @author    Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net> | ||||||
|  * @author    Brent R. Matzelle (original founder) |  * @author    Brent R. Matzelle (original founder) | ||||||
|  * @copyright 2012 - 2019 Marcus Bointon |  * @copyright 2012 - 2020 Marcus Bointon | ||||||
|  * @copyright 2010 - 2012 Jim Jagielski |  * @copyright 2010 - 2012 Jim Jagielski | ||||||
|  * @copyright 2004 - 2009 Andy Prevost |  * @copyright 2004 - 2009 Andy Prevost | ||||||
|  * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License |  * @license   http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License | ||||||
|  | @ -18,6 +19,8 @@ | ||||||
|  * FITNESS FOR A PARTICULAR PURPOSE. |  * FITNESS FOR A PARTICULAR PURPOSE. | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|  | namespace PHPMailer\PHPMailer; | ||||||
|  | 
 | ||||||
| /** | /** | ||||||
|  * PHPMailer RFC821 SMTP email transport class. |  * PHPMailer RFC821 SMTP email transport class. | ||||||
|  * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. |  * Implements RFC 821 SMTP commands and provides some utility methods for sending mail to an SMTP server. | ||||||
|  | @ -32,7 +35,7 @@ class SMTP | ||||||
|      * |      * | ||||||
|      * @var string |      * @var string | ||||||
|      */ |      */ | ||||||
|     const VERSION = '6.1.5'; |     const VERSION = '6.8.0'; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * SMTP line break constant. |      * SMTP line break constant. | ||||||
|  | @ -48,6 +51,13 @@ class SMTP | ||||||
|      */ |      */ | ||||||
|     const DEFAULT_PORT = 25; |     const DEFAULT_PORT = 25; | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * The SMTPs port to use if one is not specified. | ||||||
|  |      * | ||||||
|  |      * @var int | ||||||
|  |      */ | ||||||
|  |     const DEFAULT_SECURE_PORT = 465; | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * The maximum line length allowed by RFC 5321 section 4.5.3.1.6, |      * The maximum line length allowed by RFC 5321 section 4.5.3.1.6, | ||||||
|      * *excluding* a trailing CRLF break. |      * *excluding* a trailing CRLF break. | ||||||
|  | @ -183,6 +193,9 @@ class SMTP | ||||||
|         'Amazon_SES' => '/[\d]{3} Ok (.*)/', |         'Amazon_SES' => '/[\d]{3} Ok (.*)/', | ||||||
|         'SendGrid' => '/[\d]{3} Ok: queued as (.*)/', |         'SendGrid' => '/[\d]{3} Ok: queued as (.*)/', | ||||||
|         'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/', |         'CampaignMonitor' => '/[\d]{3} 2.0.0 OK:([a-zA-Z\d]{48})/', | ||||||
|  |         'Haraka' => '/[\d]{3} Message Queued \((.*)\)/', | ||||||
|  |         'ZoneMTA' => '/[\d]{3} Message queued as (.*)/', | ||||||
|  |         'Mailjet' => '/[\d]{3} OK queued as (.*)/', | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -309,17 +322,11 @@ class SMTP | ||||||
|      */ |      */ | ||||||
|     public function connect($host, $port = null, $timeout = 30, $options = []) |     public function connect($host, $port = null, $timeout = 30, $options = []) | ||||||
|     { |     { | ||||||
|         static $streamok; |         //Clear errors to avoid confusion
 | ||||||
|         //This is enabled by default since 5.0.0 but some providers disable it
 |  | ||||||
|         //Check this once and cache the result
 |  | ||||||
|         if (null === $streamok) { |  | ||||||
|             $streamok = function_exists('stream_socket_client'); |  | ||||||
|         } |  | ||||||
|         // Clear errors to avoid confusion
 |  | ||||||
|         $this->setError(''); |         $this->setError(''); | ||||||
|         // Make sure we are __not__ connected
 |         //Make sure we are __not__ connected
 | ||||||
|         if ($this->connected()) { |         if ($this->connected()) { | ||||||
|             // Already connected, generate error
 |             //Already connected, generate error
 | ||||||
|             $this->setError('Already connected to a server'); |             $this->setError('Already connected to a server'); | ||||||
| 
 | 
 | ||||||
|             return false; |             return false; | ||||||
|  | @ -327,18 +334,66 @@ class SMTP | ||||||
|         if (empty($port)) { |         if (empty($port)) { | ||||||
|             $port = self::DEFAULT_PORT; |             $port = self::DEFAULT_PORT; | ||||||
|         } |         } | ||||||
|         // Connect to the SMTP server
 |         //Connect to the SMTP server
 | ||||||
|         $this->edebug( |         $this->edebug( | ||||||
|             "Connection: opening to $host:$port, timeout=$timeout, options=" . |             "Connection: opening to $host:$port, timeout=$timeout, options=" . | ||||||
|             (count($options) > 0 ? var_export($options, true) : 'array()'), |             (count($options) > 0 ? var_export($options, true) : 'array()'), | ||||||
|             self::DEBUG_CONNECTION |             self::DEBUG_CONNECTION | ||||||
|         ); |         ); | ||||||
|  | 
 | ||||||
|  |         $this->smtp_conn = $this->getSMTPConnection($host, $port, $timeout, $options); | ||||||
|  | 
 | ||||||
|  |         if ($this->smtp_conn === false) { | ||||||
|  |             //Error info already set inside `getSMTPConnection()`
 | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         $this->edebug('Connection: opened', self::DEBUG_CONNECTION); | ||||||
|  | 
 | ||||||
|  |         //Get any announcement
 | ||||||
|  |         $this->last_reply = $this->get_lines(); | ||||||
|  |         $this->edebug('SERVER -> CLIENT: ' . $this->last_reply, self::DEBUG_SERVER); | ||||||
|  |         $responseCode = (int)substr($this->last_reply, 0, 3); | ||||||
|  |         if ($responseCode === 220) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         //Anything other than a 220 response means something went wrong
 | ||||||
|  |         //RFC 5321 says the server will wait for us to send a QUIT in response to a 554 error
 | ||||||
|  |         //https://tools.ietf.org/html/rfc5321#section-3.1
 | ||||||
|  |         if ($responseCode === 554) { | ||||||
|  |             $this->quit(); | ||||||
|  |         } | ||||||
|  |         //This will handle 421 responses which may not wait for a QUIT (e.g. if the server is being shut down)
 | ||||||
|  |         $this->edebug('Connection: closing due to error', self::DEBUG_CONNECTION); | ||||||
|  |         $this->close(); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Create connection to the SMTP server. | ||||||
|  |      * | ||||||
|  |      * @param string $host    SMTP server IP or host name | ||||||
|  |      * @param int    $port    The port number to connect to | ||||||
|  |      * @param int    $timeout How long to wait for the connection to open | ||||||
|  |      * @param array  $options An array of options for stream_context_create() | ||||||
|  |      * | ||||||
|  |      * @return false|resource | ||||||
|  |      */ | ||||||
|  |     protected function getSMTPConnection($host, $port = null, $timeout = 30, $options = []) | ||||||
|  |     { | ||||||
|  |         static $streamok; | ||||||
|  |         //This is enabled by default since 5.0.0 but some providers disable it
 | ||||||
|  |         //Check this once and cache the result
 | ||||||
|  |         if (null === $streamok) { | ||||||
|  |             $streamok = function_exists('stream_socket_client'); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         $errno = 0; |         $errno = 0; | ||||||
|         $errstr = ''; |         $errstr = ''; | ||||||
|         if ($streamok) { |         if ($streamok) { | ||||||
|             $socket_context = stream_context_create($options); |             $socket_context = stream_context_create($options); | ||||||
|             set_error_handler([$this, 'errorHandler']); |             set_error_handler([$this, 'errorHandler']); | ||||||
|             $this->smtp_conn = stream_socket_client( |             $connection = stream_socket_client( | ||||||
|                 $host . ':' . $port, |                 $host . ':' . $port, | ||||||
|                 $errno, |                 $errno, | ||||||
|                 $errstr, |                 $errstr, | ||||||
|  | @ -346,7 +401,6 @@ class SMTP | ||||||
|                 STREAM_CLIENT_CONNECT, |                 STREAM_CLIENT_CONNECT, | ||||||
|                 $socket_context |                 $socket_context | ||||||
|             ); |             ); | ||||||
|             restore_error_handler(); |  | ||||||
|         } else { |         } else { | ||||||
|             //Fall back to fsockopen which should work in more places, but is missing some features
 |             //Fall back to fsockopen which should work in more places, but is missing some features
 | ||||||
|             $this->edebug( |             $this->edebug( | ||||||
|  | @ -354,17 +408,18 @@ class SMTP | ||||||
|                 self::DEBUG_CONNECTION |                 self::DEBUG_CONNECTION | ||||||
|             ); |             ); | ||||||
|             set_error_handler([$this, 'errorHandler']); |             set_error_handler([$this, 'errorHandler']); | ||||||
|             $this->smtp_conn = fsockopen( |             $connection = fsockopen( | ||||||
|                 $host, |                 $host, | ||||||
|                 $port, |                 $port, | ||||||
|                 $errno, |                 $errno, | ||||||
|                 $errstr, |                 $errstr, | ||||||
|                 $timeout |                 $timeout | ||||||
|             ); |             ); | ||||||
|             restore_error_handler(); |  | ||||||
|         } |         } | ||||||
|         // Verify we connected properly
 |         restore_error_handler(); | ||||||
|         if (!is_resource($this->smtp_conn)) { | 
 | ||||||
|  |         //Verify we connected properly
 | ||||||
|  |         if (!is_resource($connection)) { | ||||||
|             $this->setError( |             $this->setError( | ||||||
|                 'Failed to connect to server', |                 'Failed to connect to server', | ||||||
|                 '', |                 '', | ||||||
|  | @ -379,22 +434,19 @@ class SMTP | ||||||
| 
 | 
 | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
|         $this->edebug('Connection: opened', self::DEBUG_CONNECTION); | 
 | ||||||
|         // SMTP server can take longer to respond, give longer timeout for first read
 |         //SMTP server can take longer to respond, give longer timeout for first read
 | ||||||
|         // Windows does not have support for this timeout function
 |         //Windows does not have support for this timeout function
 | ||||||
|         if (strpos(PHP_OS, 'WIN') !== 0) { |         if (strpos(PHP_OS, 'WIN') !== 0) { | ||||||
|             $max = (int) ini_get('max_execution_time'); |             $max = (int)ini_get('max_execution_time'); | ||||||
|             // Don't bother if unlimited
 |             //Don't bother if unlimited, or if set_time_limit is disabled
 | ||||||
|             if (0 !== $max && $timeout > $max) { |             if (0 !== $max && $timeout > $max && strpos(ini_get('disable_functions'), 'set_time_limit') === false) { | ||||||
|                 @set_time_limit($timeout); |                 @set_time_limit($timeout); | ||||||
|             } |             } | ||||||
|             stream_set_timeout($this->smtp_conn, $timeout, 0); |             stream_set_timeout($connection, $timeout, 0); | ||||||
|         } |         } | ||||||
|         // Get any announcement
 |  | ||||||
|         $announce = $this->get_lines(); |  | ||||||
|         $this->edebug('SERVER -> CLIENT: ' . $announce, self::DEBUG_SERVER); |  | ||||||
| 
 | 
 | ||||||
|         return true; |         return $connection; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -418,7 +470,7 @@ class SMTP | ||||||
|             $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; |             $crypto_method |= STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Begin encrypted connection
 |         //Begin encrypted connection
 | ||||||
|         set_error_handler([$this, 'errorHandler']); |         set_error_handler([$this, 'errorHandler']); | ||||||
|         $crypto_ok = stream_socket_enable_crypto( |         $crypto_ok = stream_socket_enable_crypto( | ||||||
|             $this->smtp_conn, |             $this->smtp_conn, | ||||||
|  | @ -439,7 +491,7 @@ class SMTP | ||||||
|      * @param string $username The user name |      * @param string $username The user name | ||||||
|      * @param string $password The password |      * @param string $password The password | ||||||
|      * @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2) |      * @param string $authtype The auth type (CRAM-MD5, PLAIN, LOGIN, XOAUTH2) | ||||||
|      * @param OAuth  $OAuth    An optional OAuth instance for XOAUTH2 authentication |      * @param OAuthTokenProvider $OAuth An optional OAuthTokenProvider instance for XOAUTH2 authentication | ||||||
|      * |      * | ||||||
|      * @return bool True if successfully authenticated |      * @return bool True if successfully authenticated | ||||||
|      */ |      */ | ||||||
|  | @ -456,11 +508,11 @@ class SMTP | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (array_key_exists('EHLO', $this->server_caps)) { |         if (array_key_exists('EHLO', $this->server_caps)) { | ||||||
|             // SMTP extensions are available; try to find a proper authentication method
 |             //SMTP extensions are available; try to find a proper authentication method
 | ||||||
|             if (!array_key_exists('AUTH', $this->server_caps)) { |             if (!array_key_exists('AUTH', $this->server_caps)) { | ||||||
|                 $this->setError('Authentication is not allowed at this stage'); |                 $this->setError('Authentication is not allowed at this stage'); | ||||||
|                 // 'at this stage' means that auth may be allowed after the stage changes
 |                 //'at this stage' means that auth may be allowed after the stage changes
 | ||||||
|                 // e.g. after STARTTLS
 |                 //e.g. after STARTTLS
 | ||||||
| 
 | 
 | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|  | @ -504,22 +556,25 @@ class SMTP | ||||||
|         } |         } | ||||||
|         switch ($authtype) { |         switch ($authtype) { | ||||||
|             case 'PLAIN': |             case 'PLAIN': | ||||||
|                 // Start authentication
 |                 //Start authentication
 | ||||||
|                 if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { |                 if (!$this->sendCommand('AUTH', 'AUTH PLAIN', 334)) { | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
|                 // Send encoded username and password
 |                 //Send encoded username and password
 | ||||||
|                 if (!$this->sendCommand( |                 if ( | ||||||
|                     'User & Password', |                     //Format from https://tools.ietf.org/html/rfc4616#section-2
 | ||||||
|                     base64_encode("\0" . $username . "\0" . $password), |                     //We skip the first field (it's forgery), so the string starts with a null byte
 | ||||||
|                     235 |                     !$this->sendCommand( | ||||||
|                 ) |                         'User & Password', | ||||||
|  |                         base64_encode("\0" . $username . "\0" . $password), | ||||||
|  |                         235 | ||||||
|  |                     ) | ||||||
|                 ) { |                 ) { | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|             case 'LOGIN': |             case 'LOGIN': | ||||||
|                 // Start authentication
 |                 //Start authentication
 | ||||||
|                 if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { |                 if (!$this->sendCommand('AUTH', 'AUTH LOGIN', 334)) { | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
|  | @ -531,17 +586,17 @@ class SMTP | ||||||
|                 } |                 } | ||||||
|                 break; |                 break; | ||||||
|             case 'CRAM-MD5': |             case 'CRAM-MD5': | ||||||
|                 // Start authentication
 |                 //Start authentication
 | ||||||
|                 if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { |                 if (!$this->sendCommand('AUTH CRAM-MD5', 'AUTH CRAM-MD5', 334)) { | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
|                 // Get the challenge
 |                 //Get the challenge
 | ||||||
|                 $challenge = base64_decode(substr($this->last_reply, 4)); |                 $challenge = base64_decode(substr($this->last_reply, 4)); | ||||||
| 
 | 
 | ||||||
|                 // Build the response
 |                 //Build the response
 | ||||||
|                 $response = $username . ' ' . $this->hmac($challenge, $password); |                 $response = $username . ' ' . $this->hmac($challenge, $password); | ||||||
| 
 | 
 | ||||||
|                 // send encoded credentials
 |                 //send encoded credentials
 | ||||||
|                 return $this->sendCommand('Username', base64_encode($response), 235); |                 return $this->sendCommand('Username', base64_encode($response), 235); | ||||||
|             case 'XOAUTH2': |             case 'XOAUTH2': | ||||||
|                 //The OAuth instance must be set up prior to requesting auth.
 |                 //The OAuth instance must be set up prior to requesting auth.
 | ||||||
|  | @ -550,7 +605,7 @@ class SMTP | ||||||
|                 } |                 } | ||||||
|                 $oauth = $OAuth->getOauth64(); |                 $oauth = $OAuth->getOauth64(); | ||||||
| 
 | 
 | ||||||
|                 // Start authentication
 |                 //Start authentication
 | ||||||
|                 if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { |                 if (!$this->sendCommand('AUTH', 'AUTH XOAUTH2 ' . $oauth, 235)) { | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
|  | @ -580,15 +635,15 @@ class SMTP | ||||||
|             return hash_hmac('md5', $data, $key); |             return hash_hmac('md5', $data, $key); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // The following borrowed from
 |         //The following borrowed from
 | ||||||
|         // http://php.net/manual/en/function.mhash.php#27225
 |         //http://php.net/manual/en/function.mhash.php#27225
 | ||||||
| 
 | 
 | ||||||
|         // RFC 2104 HMAC implementation for php.
 |         //RFC 2104 HMAC implementation for php.
 | ||||||
|         // Creates an md5 HMAC.
 |         //Creates an md5 HMAC.
 | ||||||
|         // Eliminates the need to install mhash to compute a HMAC
 |         //Eliminates the need to install mhash to compute a HMAC
 | ||||||
|         // by Lance Rushing
 |         //by Lance Rushing
 | ||||||
| 
 | 
 | ||||||
|         $bytelen = 64; // byte length for md5
 |         $bytelen = 64; //byte length for md5
 | ||||||
|         if (strlen($key) > $bytelen) { |         if (strlen($key) > $bytelen) { | ||||||
|             $key = pack('H*', md5($key)); |             $key = pack('H*', md5($key)); | ||||||
|         } |         } | ||||||
|  | @ -611,7 +666,7 @@ class SMTP | ||||||
|         if (is_resource($this->smtp_conn)) { |         if (is_resource($this->smtp_conn)) { | ||||||
|             $sock_status = stream_get_meta_data($this->smtp_conn); |             $sock_status = stream_get_meta_data($this->smtp_conn); | ||||||
|             if ($sock_status['eof']) { |             if ($sock_status['eof']) { | ||||||
|                 // The socket is valid but we are not connected
 |                 //The socket is valid but we are not connected
 | ||||||
|                 $this->edebug( |                 $this->edebug( | ||||||
|                     'SMTP NOTICE: EOF caught while checking if connected', |                     'SMTP NOTICE: EOF caught while checking if connected', | ||||||
|                     self::DEBUG_CLIENT |                     self::DEBUG_CLIENT | ||||||
|  | @ -621,7 +676,7 @@ class SMTP | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             return true; // everything looks good
 |             return true; //everything looks good
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return false; |         return false; | ||||||
|  | @ -635,11 +690,10 @@ class SMTP | ||||||
|      */ |      */ | ||||||
|     public function close() |     public function close() | ||||||
|     { |     { | ||||||
|         $this->setError(''); |  | ||||||
|         $this->server_caps = null; |         $this->server_caps = null; | ||||||
|         $this->helo_rply = null; |         $this->helo_rply = null; | ||||||
|         if (is_resource($this->smtp_conn)) { |         if (is_resource($this->smtp_conn)) { | ||||||
|             // close the connection and cleanup
 |             //Close the connection and cleanup
 | ||||||
|             fclose($this->smtp_conn); |             fclose($this->smtp_conn); | ||||||
|             $this->smtp_conn = null; //Makes for cleaner serialization
 |             $this->smtp_conn = null; //Makes for cleaner serialization
 | ||||||
|             $this->edebug('Connection: closed', self::DEBUG_CONNECTION); |             $this->edebug('Connection: closed', self::DEBUG_CONNECTION); | ||||||
|  | @ -649,7 +703,7 @@ class SMTP | ||||||
|     /** |     /** | ||||||
|      * Send an SMTP DATA command. |      * Send an SMTP DATA command. | ||||||
|      * Issues a data command and sends the msg_data to the server, |      * Issues a data command and sends the msg_data to the server, | ||||||
|      * finializing the mail transaction. $msg_data is the message |      * finalizing the mail transaction. $msg_data is the message | ||||||
|      * that is to be send with the headers. Each header needs to be |      * that is to be send with the headers. Each header needs to be | ||||||
|      * on a single line followed by a <CRLF> with the message headers |      * on a single line followed by a <CRLF> with the message headers | ||||||
|      * and the message body being separated by an additional <CRLF>. |      * and the message body being separated by an additional <CRLF>. | ||||||
|  | @ -674,7 +728,7 @@ class SMTP | ||||||
|          * NOTE: this does not count towards line-length limit. |          * NOTE: this does not count towards line-length limit. | ||||||
|          */ |          */ | ||||||
| 
 | 
 | ||||||
|         // Normalize line breaks before exploding
 |         //Normalize line breaks before exploding
 | ||||||
|         $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data)); |         $lines = explode("\n", str_replace(["\r\n", "\r"], "\n", $msg_data)); | ||||||
| 
 | 
 | ||||||
|         /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field |         /* To distinguish between a complete RFC822 message and a plain message body, we check if the first field | ||||||
|  | @ -720,7 +774,8 @@ class SMTP | ||||||
| 
 | 
 | ||||||
|             //Send the lines to the server
 |             //Send the lines to the server
 | ||||||
|             foreach ($lines_out as $line_out) { |             foreach ($lines_out as $line_out) { | ||||||
|                 //RFC2821 section 4.5.2
 |                 //Dot-stuffing as per RFC5321 section 4.5.2
 | ||||||
|  |                 //https://tools.ietf.org/html/rfc5321#section-4.5.2
 | ||||||
|                 if (!empty($line_out) && $line_out[0] === '.') { |                 if (!empty($line_out) && $line_out[0] === '.') { | ||||||
|                     $line_out = '.' . $line_out; |                     $line_out = '.' . $line_out; | ||||||
|                 } |                 } | ||||||
|  | @ -754,7 +809,16 @@ class SMTP | ||||||
|     public function hello($host = '') |     public function hello($host = '') | ||||||
|     { |     { | ||||||
|         //Try extended hello first (RFC 2821)
 |         //Try extended hello first (RFC 2821)
 | ||||||
|         return $this->sendHello('EHLO', $host) or $this->sendHello('HELO', $host); |         if ($this->sendHello('EHLO', $host)) { | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Some servers shut down the SMTP service here (RFC 5321)
 | ||||||
|  |         if (substr($this->helo_rply, 0, 3) == '421') { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return $this->sendHello('HELO', $host); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -944,12 +1008,12 @@ class SMTP | ||||||
|         $this->client_send($commandstring . static::LE, $command); |         $this->client_send($commandstring . static::LE, $command); | ||||||
| 
 | 
 | ||||||
|         $this->last_reply = $this->get_lines(); |         $this->last_reply = $this->get_lines(); | ||||||
|         // Fetch SMTP code and possible error code explanation
 |         //Fetch SMTP code and possible error code explanation
 | ||||||
|         $matches = []; |         $matches = []; | ||||||
|         if (preg_match('/^([\d]{3})[ -](?:([\d]\\.[\d]\\.[\d]{1,2}) )?/', $this->last_reply, $matches)) { |         if (preg_match('/^([\d]{3})[ -](?:([\d]\\.[\d]\\.[\d]{1,2}) )?/', $this->last_reply, $matches)) { | ||||||
|             $code = (int) $matches[1]; |             $code = (int) $matches[1]; | ||||||
|             $code_ex = (count($matches) > 2 ? $matches[2] : null); |             $code_ex = (count($matches) > 2 ? $matches[2] : null); | ||||||
|             // Cut off error code from each response line
 |             //Cut off error code from each response line
 | ||||||
|             $detail = preg_replace( |             $detail = preg_replace( | ||||||
|                 "/{$code}[ -]" . |                 "/{$code}[ -]" . | ||||||
|                 ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m', |                 ($code_ex ? str_replace('.', '\\.', $code_ex) . ' ' : '') . '/m', | ||||||
|  | @ -957,7 +1021,7 @@ class SMTP | ||||||
|                 $this->last_reply |                 $this->last_reply | ||||||
|             ); |             ); | ||||||
|         } else { |         } else { | ||||||
|             // Fall back to simple parsing if regex fails
 |             //Fall back to simple parsing if regex fails
 | ||||||
|             $code = (int) substr($this->last_reply, 0, 3); |             $code = (int) substr($this->last_reply, 0, 3); | ||||||
|             $code_ex = null; |             $code_ex = null; | ||||||
|             $detail = substr($this->last_reply, 4); |             $detail = substr($this->last_reply, 4); | ||||||
|  | @ -980,7 +1044,10 @@ class SMTP | ||||||
|             return false; |             return false; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         $this->setError(''); |         //Don't clear the error store when using keepalive
 | ||||||
|  |         if ($command !== 'RSET') { | ||||||
|  |             $this->setError(''); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  | @ -1056,8 +1123,10 @@ class SMTP | ||||||
|     { |     { | ||||||
|         //If SMTP transcripts are left enabled, or debug output is posted online
 |         //If SMTP transcripts are left enabled, or debug output is posted online
 | ||||||
|         //it can leak credentials, so hide credentials in all but lowest level
 |         //it can leak credentials, so hide credentials in all but lowest level
 | ||||||
|         if (self::DEBUG_LOWLEVEL > $this->do_debug && |         if ( | ||||||
|             in_array($command, ['User & Password', 'Username', 'Password'], true)) { |             self::DEBUG_LOWLEVEL > $this->do_debug && | ||||||
|  |             in_array($command, ['User & Password', 'Username', 'Password'], true) | ||||||
|  |         ) { | ||||||
|             $this->edebug('CLIENT -> SERVER: [credentials hidden]', self::DEBUG_CLIENT); |             $this->edebug('CLIENT -> SERVER: [credentials hidden]', self::DEBUG_CLIENT); | ||||||
|         } else { |         } else { | ||||||
|             $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT); |             $this->edebug('CLIENT -> SERVER: ' . $data, self::DEBUG_CLIENT); | ||||||
|  | @ -1111,7 +1180,7 @@ class SMTP | ||||||
|         if (!$this->server_caps) { |         if (!$this->server_caps) { | ||||||
|             $this->setError('No HELO/EHLO was sent'); |             $this->setError('No HELO/EHLO was sent'); | ||||||
| 
 | 
 | ||||||
|             return; |             return null; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!array_key_exists($name, $this->server_caps)) { |         if (!array_key_exists($name, $this->server_caps)) { | ||||||
|  | @ -1123,7 +1192,7 @@ class SMTP | ||||||
|             } |             } | ||||||
|             $this->setError('HELO handshake was used; No information about server extensions available'); |             $this->setError('HELO handshake was used; No information about server extensions available'); | ||||||
| 
 | 
 | ||||||
|             return; |             return null; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return $this->server_caps[$name]; |         return $this->server_caps[$name]; | ||||||
|  | @ -1150,7 +1219,7 @@ class SMTP | ||||||
|      */ |      */ | ||||||
|     protected function get_lines() |     protected function get_lines() | ||||||
|     { |     { | ||||||
|         // If the connection is bad, give up straight away
 |         //If the connection is bad, give up straight away
 | ||||||
|         if (!is_resource($this->smtp_conn)) { |         if (!is_resource($this->smtp_conn)) { | ||||||
|             return ''; |             return ''; | ||||||
|         } |         } | ||||||
|  | @ -1164,33 +1233,61 @@ class SMTP | ||||||
|         $selW = null; |         $selW = null; | ||||||
|         while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { |         while (is_resource($this->smtp_conn) && !feof($this->smtp_conn)) { | ||||||
|             //Must pass vars in here as params are by reference
 |             //Must pass vars in here as params are by reference
 | ||||||
|             if (!stream_select($selR, $selW, $selW, $this->Timelimit)) { |             //solution for signals inspired by https://github.com/symfony/symfony/pull/6540
 | ||||||
|  |             set_error_handler([$this, 'errorHandler']); | ||||||
|  |             $n = stream_select($selR, $selW, $selW, $this->Timelimit); | ||||||
|  |             restore_error_handler(); | ||||||
|  | 
 | ||||||
|  |             if ($n === false) { | ||||||
|  |                 $message = $this->getError()['detail']; | ||||||
|  | 
 | ||||||
|                 $this->edebug( |                 $this->edebug( | ||||||
|                     'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', |                     'SMTP -> get_lines(): select failed (' . $message . ')', | ||||||
|  |                     self::DEBUG_LOWLEVEL | ||||||
|  |                 ); | ||||||
|  | 
 | ||||||
|  |                 //stream_select returns false when the `select` system call is interrupted
 | ||||||
|  |                 //by an incoming signal, try the select again
 | ||||||
|  |                 if (stripos($message, 'interrupted system call') !== false) { | ||||||
|  |                     $this->edebug( | ||||||
|  |                         'SMTP -> get_lines(): retrying stream_select', | ||||||
|  |                         self::DEBUG_LOWLEVEL | ||||||
|  |                     ); | ||||||
|  |                     $this->setError(''); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (!$n) { | ||||||
|  |                 $this->edebug( | ||||||
|  |                     'SMTP -> get_lines(): select timed-out in (' . $this->Timelimit . ' sec)', | ||||||
|                     self::DEBUG_LOWLEVEL |                     self::DEBUG_LOWLEVEL | ||||||
|                 ); |                 ); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|             //Deliberate noise suppression - errors are handled afterwards
 |             //Deliberate noise suppression - errors are handled afterwards
 | ||||||
|             $str = @fgets($this->smtp_conn, self::MAX_REPLY_LENGTH); |             $str = @fgets($this->smtp_conn, self::MAX_REPLY_LENGTH); | ||||||
|             $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL); |             $this->edebug('SMTP INBOUND: "' . trim($str) . '"', self::DEBUG_LOWLEVEL); | ||||||
|             $data .= $str; |             $data .= $str; | ||||||
|             // If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
 |             //If response is only 3 chars (not valid, but RFC5321 S4.2 says it must be handled),
 | ||||||
|             // or 4th character is a space or a line break char, we are done reading, break the loop.
 |             //or 4th character is a space or a line break char, we are done reading, break the loop.
 | ||||||
|             // String array access is a significant micro-optimisation over strlen
 |             //String array access is a significant micro-optimisation over strlen
 | ||||||
|             if (!isset($str[3]) || $str[3] === ' ' || $str[3] === "\r" || $str[3] === "\n") { |             if (!isset($str[3]) || $str[3] === ' ' || $str[3] === "\r" || $str[3] === "\n") { | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             // Timed-out? Log and break
 |             //Timed-out? Log and break
 | ||||||
|             $info = stream_get_meta_data($this->smtp_conn); |             $info = stream_get_meta_data($this->smtp_conn); | ||||||
|             if ($info['timed_out']) { |             if ($info['timed_out']) { | ||||||
|                 $this->edebug( |                 $this->edebug( | ||||||
|                     'SMTP -> get_lines(): timed-out (' . $this->Timeout . ' sec)', |                     'SMTP -> get_lines(): stream timed-out (' . $this->Timeout . ' sec)', | ||||||
|                     self::DEBUG_LOWLEVEL |                     self::DEBUG_LOWLEVEL | ||||||
|                 ); |                 ); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|             // Now check if reads took too long
 |             //Now check if reads took too long
 | ||||||
|             if ($endtime && time() > $endtime) { |             if ($endtime && time() > $endtime) { | ||||||
|                 $this->edebug( |                 $this->edebug( | ||||||
|                     'SMTP -> get_lines(): timelimit reached (' . |                     'SMTP -> get_lines(): timelimit reached (' . | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Anonymous Contributor
						Anonymous Contributor