/framework/vendor/zend/Zend/Mail/Protocol/Smtp.php
PHP | 443 lines | 166 code | 66 blank | 211 comment | 19 complexity | ccb525c91ea87e6db5053a990141ad3c MD5 | raw file
1<?php 2 3/** 4 * Zend Framework 5 * 6 * LICENSE 7 * 8 * This source file is subject to the new BSD license that is bundled 9 * with this package in the file LICENSE.txt. 10 * It is also available through the world-wide-web at this URL: 11 * http://framework.zend.com/license/new-bsd 12 * If you did not receive a copy of the license and are unable to 13 * obtain it through the world-wide-web, please send an email 14 * to license@zend.com so we can send you a copy immediately. 15 * 16 * @category Zend 17 * @package Zend_Mail 18 * @subpackage Protocol 19 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) 20 * @license http://framework.zend.com/license/new-bsd New BSD License 21 * @version $Id: Smtp.php 20096 2010-01-06 02:05:09Z bkarwin $ 22 */ 23 24 25/** 26 * @see Zend_Mime 27 */ 28require_once 'Zend/Mime.php'; 29 30 31/** 32 * @see Zend_Mail_Protocol_Abstract 33 */ 34require_once 'Zend/Mail/Protocol/Abstract.php'; 35 36 37/** 38 * Smtp implementation of Zend_Mail_Protocol_Abstract 39 * 40 * Minimum implementation according to RFC2821: EHLO, MAIL FROM, RCPT TO, DATA, RSET, NOOP, QUIT 41 * 42 * @category Zend 43 * @package Zend_Mail 44 * @subpackage Protocol 45 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) 46 * @license http://framework.zend.com/license/new-bsd New BSD License 47 */ 48class Zend_Mail_Protocol_Smtp extends Zend_Mail_Protocol_Abstract 49{ 50 /** 51 * The transport method for the socket 52 * 53 * @var string 54 */ 55 protected $_transport = 'tcp'; 56 57 58 /** 59 * Indicates that a session is requested to be secure 60 * 61 * @var string 62 */ 63 protected $_secure; 64 65 66 /** 67 * Indicates an smtp session has been started by the HELO command 68 * 69 * @var boolean 70 */ 71 protected $_sess = false; 72 73 74 /** 75 * Indicates the HELO command has been issues 76 * 77 * @var unknown_type 78 */ 79 protected $_helo = false; 80 81 82 /** 83 * Indicates an smtp AUTH has been issued and authenticated 84 * 85 * @var unknown_type 86 */ 87 protected $_auth = false; 88 89 90 /** 91 * Indicates a MAIL command has been issued 92 * 93 * @var unknown_type 94 */ 95 protected $_mail = false; 96 97 98 /** 99 * Indicates one or more RCTP commands have been issued 100 * 101 * @var unknown_type 102 */ 103 protected $_rcpt = false; 104 105 106 /** 107 * Indicates that DATA has been issued and sent 108 * 109 * @var unknown_type 110 */ 111 protected $_data = null; 112 113 114 /** 115 * Constructor. 116 * 117 * @param string $host 118 * @param integer $port 119 * @param array $config 120 * @return void 121 * @throws Zend_Mail_Protocol_Exception 122 */ 123 public function __construct($host = '127.0.0.1', $port = null, array $config = array()) 124 { 125 if (isset($config['ssl'])) { 126 switch (strtolower($config['ssl'])) { 127 case 'tls': 128 $this->_secure = 'tls'; 129 break; 130 131 case 'ssl': 132 $this->_transport = 'ssl'; 133 $this->_secure = 'ssl'; 134 if ($port == null) { 135 $port = 465; 136 } 137 break; 138 139 default: 140 /** 141 * @see Zend_Mail_Protocol_Exception 142 */ 143 require_once 'Zend/Mail/Protocol/Exception.php'; 144 throw new Zend_Mail_Protocol_Exception($config['ssl'] . ' is unsupported SSL type'); 145 break; 146 } 147 } 148 149 // If no port has been specified then check the master PHP ini file. Defaults to 25 if the ini setting is null. 150 if ($port == null) { 151 if (($port = ini_get('smtp_port')) == '') { 152 $port = 25; 153 } 154 } 155 156 parent::__construct($host, $port); 157 } 158 159 160 /** 161 * Connect to the server with the parameters given in the constructor. 162 * 163 * @return boolean 164 */ 165 public function connect() 166 { 167 return $this->_connect($this->_transport . '://' . $this->_host . ':'. $this->_port); 168 } 169 170 171 /** 172 * Initiate HELO/EHLO sequence and set flag to indicate valid smtp session 173 * 174 * @param string $host The client hostname or IP address (default: 127.0.0.1) 175 * @throws Zend_Mail_Protocol_Exception 176 * @return void 177 */ 178 public function helo($host = '127.0.0.1') 179 { 180 // Respect RFC 2821 and disallow HELO attempts if session is already initiated. 181 if ($this->_sess === true) { 182 /** 183 * @see Zend_Mail_Protocol_Exception 184 */ 185 require_once 'Zend/Mail/Protocol/Exception.php'; 186 throw new Zend_Mail_Protocol_Exception('Cannot issue HELO to existing session'); 187 } 188 189 // Validate client hostname 190 if (!$this->_validHost->isValid($host)) { 191 /** 192 * @see Zend_Mail_Protocol_Exception 193 */ 194 require_once 'Zend/Mail/Protocol/Exception.php'; 195 throw new Zend_Mail_Protocol_Exception(join(', ', $this->_validHost->getMessages())); 196 } 197 198 // Initiate helo sequence 199 $this->_expect(220, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 200 $this->_ehlo($host); 201 202 // If a TLS session is required, commence negotiation 203 if ($this->_secure == 'tls') { 204 $this->_send('STARTTLS'); 205 $this->_expect(220, 180); 206 if (!stream_socket_enable_crypto($this->_socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) { 207 /** 208 * @see Zend_Mail_Protocol_Exception 209 */ 210 require_once 'Zend/Mail/Protocol/Exception.php'; 211 throw new Zend_Mail_Protocol_Exception('Unable to connect via TLS'); 212 } 213 $this->_ehlo($host); 214 } 215 216 $this->_startSession(); 217 $this->auth(); 218 } 219 220 221 /** 222 * Send EHLO or HELO depending on capabilities of smtp host 223 * 224 * @param string $host The client hostname or IP address (default: 127.0.0.1) 225 * @throws Zend_Mail_Protocol_Exception 226 * @return void 227 */ 228 protected function _ehlo($host) 229 { 230 // Support for older, less-compliant remote servers. Tries multiple attempts of EHLO or HELO. 231 try { 232 $this->_send('EHLO ' . $host); 233 $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 234 } catch (Zend_Mail_Protocol_Exception $e) { 235 $this->_send('HELO ' . $host); 236 $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 237 } catch (Zend_Mail_Protocol_Exception $e) { 238 throw $e; 239 } 240 } 241 242 243 /** 244 * Issues MAIL command 245 * 246 * @param string $from Sender mailbox 247 * @throws Zend_Mail_Protocol_Exception 248 * @return void 249 */ 250 public function mail($from) 251 { 252 if ($this->_sess !== true) { 253 /** 254 * @see Zend_Mail_Protocol_Exception 255 */ 256 require_once 'Zend/Mail/Protocol/Exception.php'; 257 throw new Zend_Mail_Protocol_Exception('A valid session has not been started'); 258 } 259 260 $this->_send('MAIL FROM:<' . $from . '>'); 261 $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 262 263 // Set mail to true, clear recipients and any existing data flags as per 4.1.1.2 of RFC 2821 264 $this->_mail = true; 265 $this->_rcpt = false; 266 $this->_data = false; 267 } 268 269 270 /** 271 * Issues RCPT command 272 * 273 * @param string $to Receiver(s) mailbox 274 * @throws Zend_Mail_Protocol_Exception 275 * @return void 276 */ 277 public function rcpt($to) 278 { 279 if ($this->_mail !== true) { 280 /** 281 * @see Zend_Mail_Protocol_Exception 282 */ 283 require_once 'Zend/Mail/Protocol/Exception.php'; 284 throw new Zend_Mail_Protocol_Exception('No sender reverse path has been supplied'); 285 } 286 287 // Set rcpt to true, as per 4.1.1.3 of RFC 2821 288 $this->_send('RCPT TO:<' . $to . '>'); 289 $this->_expect(array(250, 251), 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 290 $this->_rcpt = true; 291 } 292 293 294 /** 295 * Issues DATA command 296 * 297 * @param string $data 298 * @throws Zend_Mail_Protocol_Exception 299 * @return void 300 */ 301 public function data($data) 302 { 303 // Ensure recipients have been set 304 if ($this->_rcpt !== true) { 305 /** 306 * @see Zend_Mail_Protocol_Exception 307 */ 308 require_once 'Zend/Mail/Protocol/Exception.php'; 309 throw new Zend_Mail_Protocol_Exception('No recipient forward path has been supplied'); 310 } 311 312 $this->_send('DATA'); 313 $this->_expect(354, 120); // Timeout set for 2 minutes as per RFC 2821 4.5.3.2 314 315 foreach (explode(Zend_Mime::LINEEND, $data) as $line) { 316 if (strpos($line, '.') === 0) { 317 // Escape lines prefixed with a '.' 318 $line = '.' . $line; 319 } 320 $this->_send($line); 321 } 322 323 $this->_send('.'); 324 $this->_expect(250, 600); // Timeout set for 10 minutes as per RFC 2821 4.5.3.2 325 $this->_data = true; 326 } 327 328 329 /** 330 * Issues the RSET command end validates answer 331 * 332 * Can be used to restore a clean smtp communication state when a transaction has been cancelled or commencing a new transaction. 333 * 334 * @return void 335 */ 336 public function rset() 337 { 338 $this->_send('RSET'); 339 // MS ESMTP doesn't follow RFC, see [ZF-1377] 340 $this->_expect(array(250, 220)); 341 342 $this->_mail = false; 343 $this->_rcpt = false; 344 $this->_data = false; 345 } 346 347 348 /** 349 * Issues the NOOP command end validates answer 350 * 351 * Not used by Zend_Mail, could be used to keep a connection alive or check if it is still open. 352 * 353 * @return void 354 */ 355 public function noop() 356 { 357 $this->_send('NOOP'); 358 $this->_expect(250, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 359 } 360 361 362 /** 363 * Issues the VRFY command end validates answer 364 * 365 * Not used by Zend_Mail. 366 * 367 * @param string $user User Name or eMail to verify 368 * @return void 369 */ 370 public function vrfy($user) 371 { 372 $this->_send('VRFY ' . $user); 373 $this->_expect(array(250, 251, 252), 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 374 } 375 376 377 /** 378 * Issues the QUIT command and clears the current session 379 * 380 * @return void 381 */ 382 public function quit() 383 { 384 if ($this->_sess) { 385 $this->_send('QUIT'); 386 $this->_expect(221, 300); // Timeout set for 5 minutes as per RFC 2821 4.5.3.2 387 $this->_stopSession(); 388 } 389 } 390 391 392 /** 393 * Default authentication method 394 * 395 * This default method is implemented by AUTH adapters to properly authenticate to a remote host. 396 * 397 * @throws Zend_Mail_Protocol_Exception 398 * @return void 399 */ 400 public function auth() 401 { 402 if ($this->_auth === true) { 403 /** 404 * @see Zend_Mail_Protocol_Exception 405 */ 406 require_once 'Zend/Mail/Protocol/Exception.php'; 407 throw new Zend_Mail_Protocol_Exception('Already authenticated for this session'); 408 } 409 } 410 411 412 /** 413 * Closes connection 414 * 415 * @return void 416 */ 417 public function disconnect() 418 { 419 $this->_disconnect(); 420 } 421 422 423 /** 424 * Start mail session 425 * 426 * @return void 427 */ 428 protected function _startSession() 429 { 430 $this->_sess = true; 431 } 432 433 434 /** 435 * Stop mail session 436 * 437 * @return void 438 */ 439 protected function _stopSession() 440 { 441 $this->_sess = false; 442 } 443}