|
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 */ |
|
28 require_once 'Zend/Mime.php'; |
|
29 |
|
30 |
|
31 /** |
|
32 * @see Zend_Mail_Protocol_Abstract |
|
33 */ |
|
34 require_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 */ |
|
48 class 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 } |