Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
16591 anikendra 1
<?php
2
/**
3
 * Send mail using SMTP protocol
4
 *
5
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
6
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
7
 *
8
 * Licensed under The MIT License
9
 * For full copyright and license information, please see the LICENSE.txt
10
 * Redistributions of files must retain the above copyright notice.
11
 *
12
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
13
 * @link          http://cakephp.org CakePHP(tm) Project
14
 * @package       Cake.Network.Email
15
 * @since         CakePHP(tm) v 2.0.0
16
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
17
 */
18
 
19
App::uses('CakeSocket', 'Network');
20
 
21
/**
22
 * Send mail using SMTP protocol
23
 *
24
 * @package       Cake.Network.Email
25
 */
26
class SmtpTransport extends AbstractTransport {
27
 
28
/**
29
 * Socket to SMTP server
30
 *
31
 * @var CakeSocket
32
 */
33
	protected $_socket;
34
 
35
/**
36
 * CakeEmail
37
 *
38
 * @var CakeEmail
39
 */
40
	protected $_cakeEmail;
41
 
42
/**
43
 * Content of email to return
44
 *
45
 * @var string
46
 */
47
	protected $_content;
48
 
49
/**
50
 * The response of the last sent SMTP command.
51
 *
52
 * @var array
53
 */
54
	protected $_lastResponse = array();
55
 
56
/**
57
 * Returns the response of the last sent SMTP command.
58
 *
59
 * A response consists of one or more lines containing a response
60
 * code and an optional response message text:
61
 * ```
62
 * array(
63
 *     array(
64
 *         'code' => '250',
65
 *         'message' => 'mail.example.com'
66
 *     ),
67
 *     array(
68
 *         'code' => '250',
69
 *         'message' => 'PIPELINING'
70
 *     ),
71
 *     array(
72
 *         'code' => '250',
73
 *         'message' => '8BITMIME'
74
 *     ),
75
 *     // etc...
76
 * )
77
 * ```
78
 *
79
 * @return array
80
 */
81
	public function getLastResponse() {
82
		return $this->_lastResponse;
83
	}
84
 
85
/**
86
 * Send mail
87
 *
88
 * @param CakeEmail $email CakeEmail
89
 * @return array
90
 * @throws SocketException
91
 */
92
	public function send(CakeEmail $email) {
93
		$this->_cakeEmail = $email;
94
 
95
		$this->_connect();
96
		$this->_auth();
97
		$this->_sendRcpt();
98
		$this->_sendData();
99
		$this->_disconnect();
100
 
101
		return $this->_content;
102
	}
103
 
104
/**
105
 * Set the configuration
106
 *
107
 * @param array $config Configuration options.
108
 * @return array Returns configs
109
 */
110
	public function config($config = null) {
111
		if ($config === null) {
112
			return $this->_config;
113
		}
114
		$default = array(
115
			'host' => 'localhost',
116
			'port' => 25,
117
			'timeout' => 30,
118
			'username' => null,
119
			'password' => null,
120
			'client' => null,
121
			'tls' => false
122
		);
123
		$this->_config = array_merge($default, $this->_config, $config);
124
		return $this->_config;
125
	}
126
 
127
/**
128
 * Parses and stores the reponse lines in `'code' => 'message'` format.
129
 *
130
 * @param array $responseLines Response lines to parse.
131
 * @return void
132
 */
133
	protected function _bufferResponseLines(array $responseLines) {
134
		$response = array();
135
		foreach ($responseLines as $responseLine) {
136
			if (preg_match('/^(\d{3})(?:[ -]+(.*))?$/', $responseLine, $match)) {
137
				$response[] = array(
138
					'code' => $match[1],
139
					'message' => isset($match[2]) ? $match[2] : null
140
				);
141
			}
142
		}
143
		$this->_lastResponse = array_merge($this->_lastResponse, $response);
144
	}
145
 
146
/**
147
 * Connect to SMTP Server
148
 *
149
 * @return void
150
 * @throws SocketException
151
 */
152
	protected function _connect() {
153
		$this->_generateSocket();
154
		if (!$this->_socket->connect()) {
155
			throw new SocketException(__d('cake_dev', 'Unable to connect to SMTP server.'));
156
		}
157
		$this->_smtpSend(null, '220');
158
 
159
		if (isset($this->_config['client'])) {
160
			$host = $this->_config['client'];
161
		} elseif ($httpHost = env('HTTP_HOST')) {
162
			list($host) = explode(':', $httpHost);
163
		} else {
164
			$host = 'localhost';
165
		}
166
 
167
		try {
168
			$this->_smtpSend("EHLO {$host}", '250');
169
			if ($this->_config['tls']) {
170
				$this->_smtpSend("STARTTLS", '220');
171
				$this->_socket->enableCrypto('tls');
172
				$this->_smtpSend("EHLO {$host}", '250');
173
			}
174
		} catch (SocketException $e) {
175
			if ($this->_config['tls']) {
176
				throw new SocketException(__d('cake_dev', 'SMTP server did not accept the connection or trying to connect to non TLS SMTP server using TLS.'));
177
			}
178
			try {
179
				$this->_smtpSend("HELO {$host}", '250');
180
			} catch (SocketException $e2) {
181
				throw new SocketException(__d('cake_dev', 'SMTP server did not accept the connection.'));
182
			}
183
		}
184
	}
185
 
186
/**
187
 * Send authentication
188
 *
189
 * @return void
190
 * @throws SocketException
191
 */
192
	protected function _auth() {
193
		if (isset($this->_config['username']) && isset($this->_config['password'])) {
194
			$replyCode = $this->_smtpSend('AUTH LOGIN', '334|500|502|504');
195
			if ($replyCode == '334') {
196
				try {
197
					$this->_smtpSend(base64_encode($this->_config['username']), '334');
198
				} catch (SocketException $e) {
199
					throw new SocketException(__d('cake_dev', 'SMTP server did not accept the username.'));
200
				}
201
				try {
202
					$this->_smtpSend(base64_encode($this->_config['password']), '235');
203
				} catch (SocketException $e) {
204
					throw new SocketException(__d('cake_dev', 'SMTP server did not accept the password.'));
205
				}
206
			} elseif ($replyCode == '504') {
207
				throw new SocketException(__d('cake_dev', 'SMTP authentication method not allowed, check if SMTP server requires TLS.'));
208
			} else {
209
				throw new SocketException(__d('cake_dev', 'AUTH command not recognized or not implemented, SMTP server may not require authentication.'));
210
			}
211
		}
212
	}
213
 
214
/**
215
 * Prepares the `MAIL FROM` SMTP command.
216
 *
217
 * @param string $email The email address to send with the command.
218
 * @return string
219
 */
220
	protected function _prepareFromCmd($email) {
221
		return 'MAIL FROM:<' . $email . '>';
222
	}
223
 
224
/**
225
 * Prepares the `RCPT TO` SMTP command.
226
 *
227
 * @param string $email The email address to send with the command.
228
 * @return string
229
 */
230
	protected function _prepareRcptCmd($email) {
231
		return 'RCPT TO:<' . $email . '>';
232
	}
233
 
234
/**
235
 * Prepares the `from` email address.
236
 *
237
 * @return array
238
 */
239
	protected function _prepareFromAddress() {
240
		$from = $this->_cakeEmail->returnPath();
241
		if (empty($from)) {
242
			$from = $this->_cakeEmail->from();
243
		}
244
		return $from;
245
	}
246
 
247
/**
248
 * Prepares the recipient email addresses.
249
 *
250
 * @return array
251
 */
252
	protected function _prepareRecipientAddresses() {
253
		$to = $this->_cakeEmail->to();
254
		$cc = $this->_cakeEmail->cc();
255
		$bcc = $this->_cakeEmail->bcc();
256
		return array_merge(array_keys($to), array_keys($cc), array_keys($bcc));
257
	}
258
 
259
/**
260
 * Prepares the message headers.
261
 *
262
 * @return array
263
 */
264
	protected function _prepareMessageHeaders() {
265
		return $this->_cakeEmail->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'to', 'cc', 'subject'));
266
	}
267
 
268
/**
269
 * Prepares the message body.
270
 *
271
 * @return string
272
 */
273
	protected function _prepareMessage() {
274
		$lines = $this->_cakeEmail->message();
275
		$messages = array();
276
		foreach ($lines as $line) {
277
			if ((!empty($line)) && ($line[0] === '.')) {
278
				$messages[] = '.' . $line;
279
			} else {
280
				$messages[] = $line;
281
			}
282
		}
283
		return implode("\r\n", $messages);
284
	}
285
 
286
/**
287
 * Send emails
288
 *
289
 * @return void
290
 * @throws SocketException
291
 */
292
	protected function _sendRcpt() {
293
		$from = $this->_prepareFromAddress();
294
		$this->_smtpSend($this->_prepareFromCmd(key($from)));
295
 
296
		$emails = $this->_prepareRecipientAddresses();
297
		foreach ($emails as $email) {
298
			$this->_smtpSend($this->_prepareRcptCmd($email));
299
		}
300
	}
301
 
302
/**
303
 * Send Data
304
 *
305
 * @return void
306
 * @throws SocketException
307
 */
308
	protected function _sendData() {
309
		$this->_smtpSend('DATA', '354');
310
 
311
		$headers = $this->_headersToString($this->_prepareMessageHeaders());
312
		$message = $this->_prepareMessage();
313
 
314
		$this->_smtpSend($headers . "\r\n\r\n" . $message . "\r\n\r\n\r\n.");
315
		$this->_content = array('headers' => $headers, 'message' => $message);
316
	}
317
 
318
/**
319
 * Disconnect
320
 *
321
 * @return void
322
 * @throws SocketException
323
 */
324
	protected function _disconnect() {
325
		$this->_smtpSend('QUIT', false);
326
		$this->_socket->disconnect();
327
	}
328
 
329
/**
330
 * Helper method to generate socket
331
 *
332
 * @return void
333
 * @throws SocketException
334
 */
335
	protected function _generateSocket() {
336
		$this->_socket = new CakeSocket($this->_config);
337
	}
338
 
339
/**
340
 * Protected method for sending data to SMTP connection
341
 *
342
 * @param string $data data to be sent to SMTP server
343
 * @param string|bool $checkCode code to check for in server response, false to skip
344
 * @return void
345
 * @throws SocketException
346
 */
347
	protected function _smtpSend($data, $checkCode = '250') {
348
		$this->_lastResponse = array();
349
 
350
		if ($data !== null) {
351
			$this->_socket->write($data . "\r\n");
352
		}
353
		while ($checkCode !== false) {
354
			$response = '';
355
			$startTime = time();
356
			while (substr($response, -2) !== "\r\n" && ((time() - $startTime) < $this->_config['timeout'])) {
357
				$response .= $this->_socket->read();
358
			}
359
			if (substr($response, -2) !== "\r\n") {
360
				throw new SocketException(__d('cake_dev', 'SMTP timeout.'));
361
			}
362
			$responseLines = explode("\r\n", rtrim($response, "\r\n"));
363
			$response = end($responseLines);
364
 
365
			$this->_bufferResponseLines($responseLines);
366
 
367
			if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) {
368
				if ($code[2] === '-') {
369
					continue;
370
				}
371
				return $code[1];
372
			}
373
			throw new SocketException(__d('cake_dev', 'SMTP Error: %s', $response));
374
		}
375
	}
376
 
377
}