File indexing completed on 2025-03-02 05:29:34
0001 <?php 0002 0003 /** 0004 * Zend Framework 0005 * 0006 * LICENSE 0007 * 0008 * This source file is subject to the new BSD license that is bundled 0009 * with this package in the file LICENSE.txt. 0010 * It is also available through the world-wide-web at this URL: 0011 * http://framework.zend.com/license/new-bsd 0012 * If you did not receive a copy of the license and are unable to 0013 * obtain it through the world-wide-web, please send an email 0014 * to license@zend.com so we can send you a copy immediately. 0015 * 0016 * @category Zend 0017 * @package Zend_Mail 0018 * @subpackage Protocol 0019 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0020 * @license http://framework.zend.com/license/new-bsd New BSD License 0021 * @version $Id$ 0022 */ 0023 0024 0025 /** 0026 * @see Zend_Mime 0027 */ 0028 // require_once 'Zend/Mime.php'; 0029 0030 0031 /** 0032 * @see Zend_Mail_Protocol_Abstract 0033 */ 0034 // require_once 'Zend/Mail/Protocol/Abstract.php'; 0035 0036 0037 /** 0038 * Smtp implementation of Zend_Mail_Protocol_Abstract 0039 * 0040 * Minimum implementation according to RFC2821: EHLO, MAIL FROM, RCPT TO, DATA, RSET, NOOP, QUIT 0041 * 0042 * @category Zend 0043 * @package Zend_Mail 0044 * @subpackage Protocol 0045 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0046 * @license http://framework.zend.com/license/new-bsd New BSD License 0047 */ 0048 class Zend_Mail_Protocol_Smtp extends Zend_Mail_Protocol_Abstract 0049 { 0050 /** 0051 * The transport method for the socket 0052 * 0053 * @var string 0054 */ 0055 protected $_transport = 'tcp'; 0056 0057 0058 /** 0059 * Indicates that a session is requested to be secure 0060 * 0061 * @var string 0062 */ 0063 protected $_secure; 0064 0065 0066 /** 0067 * Indicates an smtp session has been started by the HELO command 0068 * 0069 * @var boolean 0070 */ 0071 protected $_sess = false; 0072 0073 0074 /** 0075 * Indicates the HELO command has been issues 0076 * 0077 * @var unknown_type 0078 */ 0079 protected $_helo = false; 0080 0081 0082 /** 0083 * Indicates an smtp AUTH has been issued and authenticated 0084 * 0085 * @var unknown_type 0086 */ 0087 protected $_auth = false; 0088 0089 0090 /** 0091 * Indicates a MAIL command has been issued 0092 * 0093 * @var unknown_type 0094 */ 0095 protected $_mail = false; 0096 0097 0098 /** 0099 * Indicates one or more RCTP commands have been issued 0100 * 0101 * @var unknown_type 0102 */ 0103 protected $_rcpt = false; 0104 0105 0106 /** 0107 * Indicates that DATA has been issued and sent 0108 * 0109 * @var unknown_type 0110 */ 0111 protected $_data = null; 0112 0113 0114 /** 0115 * Constructor. 0116 * 0117 * @param string $host 0118 * @param integer $port 0119 * @param array $config 0120 * @return void 0121 * @throws Zend_Mail_Protocol_Exception 0122 */ 0123 public function __construct($host = '127.0.0.1', $port = null, array $config = array()) 0124 { 0125 if (isset($config['ssl'])) { 0126 switch (strtolower($config['ssl'])) { 0127 case 'tls': 0128 $this->_secure = 'tls'; 0129 break; 0130 0131 case 'ssl': 0132 $this->_transport = 'ssl'; 0133 $this->_secure = 'ssl'; 0134 if ($port == null) { 0135 $port = 465; 0136 } 0137 break; 0138 0139 default: 0140 /** 0141 * @see Zend_Mail_Protocol_Exception 0142 */ 0143 // require_once 'Zend/Mail/Protocol/Exception.php'; 0144 throw new Zend_Mail_Protocol_Exception($config['ssl'] . ' is unsupported SSL type'); 0145 break; 0146 } 0147 } 0148 0149 // If no port has been specified then check the master PHP ini file. Defaults to 25 if the ini setting is null. 0150 if ($port == null) { 0151 if (($port = ini_get('smtp_port')) == '') { 0152 $port = 25; 0153 } 0154 } 0155 0156 parent::__construct($host, $port); 0157 } 0158 0159 0160 /** 0161 * Connect to the server with the parameters given in the constructor. 0162 * 0163 * @return boolean 0164 */ 0165 public function connect() 0166 { 0167 return $this->_connect($this->_transport . '://' . $this->_host . ':'. $this->_port); 0168 } 0169 0170 0171 /** 0172 * Initiate HELO/EHLO sequence and set flag to indicate valid smtp session 0173 * 0174 * @param string $host The client hostname or IP address (default: 127.0.0.1) 0175 * @throws Zend_Mail_Protocol_Exception 0176 * @return void 0177 */ 0178 public function helo($host = '127.0.0.1') 0179 { 0180 // Respect RFC 2821 and disallow HELO attempts if session is already initiated. 0181 if ($this->_sess === true) { 0182 /** 0183 * @see Zend_Mail_Protocol_Exception 0184 */ 0185 // require_once 'Zend/Mail/Protocol/Exception.php'; 0186 throw new Zend_Mail_Protocol_Exception('Cannot issue HELO to existing session'); 0187 } 0188 0189 // Validate client hostname 0190 if (!$this->_validHost->isValid($host)) { 0191 /** 0192 * @see Zend_Mail_Protocol_Exception 0193 */ 0194 // require_once 'Zend/Mail/Protocol/Exception.php'; 0195 throw new Zend_Mail_Protocol_Exception(join(', ', $this->_validHost->getMessages())); 0196 } 0197 0198 // Initiate helo sequence 0199 $this->_expect(220, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 0200 $this->_ehlo($host); 0201 0202 // If a TLS session is required, commence negotiation 0203 if ($this->_secure == 'tls') { 0204 $this->_send('STARTTLS'); 0205 $this->_expect(220, 180); 0206 if (!stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { 0207 /** 0208 * @see Zend_Mail_Protocol_Exception 0209 */ 0210 // require_once 'Zend/Mail/Protocol/Exception.php'; 0211 throw new Zend_Mail_Protocol_Exception('Unable to connect via TLS'); 0212 } 0213 $this->_ehlo($host); 0214 } 0215 0216 $this->_startSession(); 0217 $this->auth(); 0218 } 0219 0220 0221 /** 0222 * Send EHLO or HELO depending on capabilities of smtp host 0223 * 0224 * @param string $host The client hostname or IP address (default: 127.0.0.1) 0225 * @throws Zend_Mail_Protocol_Exception 0226 * @return void 0227 */ 0228 protected function _ehlo($host) 0229 { 0230 // Support for older, less-compliant remote servers. Tries multiple attempts of EHLO or HELO. 0231 try { 0232 $this->_send('EHLO ' . $host); 0233 $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 0234 } catch (Zend_Mail_Protocol_Exception $e) { 0235 $this->_send('HELO ' . $host); 0236 $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 0237 } catch (Zend_Mail_Protocol_Exception $e) { 0238 throw $e; 0239 } 0240 } 0241 0242 0243 /** 0244 * Issues MAIL command 0245 * 0246 * @param string $from Sender mailbox 0247 * @throws Zend_Mail_Protocol_Exception 0248 * @return void 0249 */ 0250 public function mail($from) 0251 { 0252 if ($this->_sess !== true) { 0253 /** 0254 * @see Zend_Mail_Protocol_Exception 0255 */ 0256 // require_once 'Zend/Mail/Protocol/Exception.php'; 0257 throw new Zend_Mail_Protocol_Exception('A valid session has not been started'); 0258 } 0259 0260 $this->_send('MAIL FROM:<' . $from . '>'); 0261 $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 0262 0263 // Set mail to true, clear recipients and any existing data flags as per 4.1.1.2 of RFC 2821 0264 $this->_mail = true; 0265 $this->_rcpt = false; 0266 $this->_data = false; 0267 } 0268 0269 0270 /** 0271 * Issues RCPT command 0272 * 0273 * @param string $to Receiver(s) mailbox 0274 * @throws Zend_Mail_Protocol_Exception 0275 * @return void 0276 */ 0277 public function rcpt($to) 0278 { 0279 if ($this->_mail !== true) { 0280 /** 0281 * @see Zend_Mail_Protocol_Exception 0282 */ 0283 // require_once 'Zend/Mail/Protocol/Exception.php'; 0284 throw new Zend_Mail_Protocol_Exception('No sender reverse path has been supplied'); 0285 } 0286 0287 // Set rcpt to true, as per 4.1.1.3 of RFC 2821 0288 $this->_send('RCPT TO:<' . $to . '>'); 0289 $this->_expect(array(250, 251), 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 0290 $this->_rcpt = true; 0291 } 0292 0293 0294 /** 0295 * Issues DATA command 0296 * 0297 * @param string $data 0298 * @throws Zend_Mail_Protocol_Exception 0299 * @return void 0300 */ 0301 public function data($data) 0302 { 0303 // Ensure recipients have been set 0304 if ($this->_rcpt !== true) { 0305 /** 0306 * @see Zend_Mail_Protocol_Exception 0307 */ 0308 // require_once 'Zend/Mail/Protocol/Exception.php'; 0309 throw new Zend_Mail_Protocol_Exception('No recipient forward path has been supplied'); 0310 } 0311 0312 $this->_send('DATA'); 0313 $this->_expect(354, 120); // Timeout set for 2 minutes as per RFC 2821 4.5.3.2 0314 0315 foreach (explode(Zend_Mime::LINEEND, $data) as $line) { 0316 if (strpos($line, '.') === 0) { 0317 // Escape lines prefixed with a '.' 0318 $line = '.' . $line; 0319 } 0320 $this->_send($line); 0321 } 0322 0323 $this->_send('.'); 0324 $this->_expect(250, 600); // Timeout set for 10 minutes as per RFC 2821 4.5.3.2 0325 $this->_data = true; 0326 } 0327 0328 0329 /** 0330 * Issues the RSET command and validates answer 0331 * 0332 * Can be used to restore a clean smtp communication state when a transaction has been cancelled or commencing a new transaction. 0333 * 0334 * @return void 0335 */ 0336 public function rset() 0337 { 0338 $this->_send('RSET'); 0339 // MS ESMTP doesn't follow RFC, see [ZF-1377] 0340 $this->_expect(array(250, 220)); 0341 0342 $this->_mail = false; 0343 $this->_rcpt = false; 0344 $this->_data = false; 0345 } 0346 0347 0348 /** 0349 * Issues the NOOP command and validates answer 0350 * 0351 * Not used by Zend_Mail, could be used to keep a connection alive or check if it is still open. 0352 * 0353 * @return void 0354 */ 0355 public function noop() 0356 { 0357 $this->_send('NOOP'); 0358 $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 0359 } 0360 0361 0362 /** 0363 * Issues the VRFY command and validates answer 0364 * 0365 * Not used by Zend_Mail. 0366 * 0367 * @param string $user User Name or eMail to verify 0368 * @return void 0369 */ 0370 public function vrfy($user) 0371 { 0372 $this->_send('VRFY ' . $user); 0373 $this->_expect(array(250, 251, 252), 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 0374 } 0375 0376 0377 /** 0378 * Issues the QUIT command and clears the current session 0379 * 0380 * @return void 0381 */ 0382 public function quit() 0383 { 0384 if ($this->_sess) { 0385 $this->_send('QUIT'); 0386 $this->_expect(221, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 0387 $this->_stopSession(); 0388 } 0389 } 0390 0391 0392 /** 0393 * Default authentication method 0394 * 0395 * This default method is implemented by AUTH adapters to properly authenticate to a remote host. 0396 * 0397 * @throws Zend_Mail_Protocol_Exception 0398 * @return void 0399 */ 0400 public function auth() 0401 { 0402 if ($this->_auth === true) { 0403 /** 0404 * @see Zend_Mail_Protocol_Exception 0405 */ 0406 // require_once 'Zend/Mail/Protocol/Exception.php'; 0407 throw new Zend_Mail_Protocol_Exception('Already authenticated for this session'); 0408 } 0409 } 0410 0411 0412 /** 0413 * Closes connection 0414 * 0415 * @return void 0416 */ 0417 public function disconnect() 0418 { 0419 $this->_disconnect(); 0420 } 0421 0422 0423 /** 0424 * Start mail session 0425 * 0426 * @return void 0427 */ 0428 protected function _startSession() 0429 { 0430 $this->_sess = true; 0431 } 0432 0433 0434 /** 0435 * Stop mail session 0436 * 0437 * @return void 0438 */ 0439 protected function _stopSession() 0440 { 0441 $this->_sess = false; 0442 } 0443 }