Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
12345 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
			$authRequired = $this->_smtpSend('AUTH LOGIN', '334|503');
195
			if ($authRequired == '334') {
196
				if (!$this->_smtpSend(base64_encode($this->_config['username']), '334')) {
197
					throw new SocketException(__d('cake_dev', 'SMTP server did not accept the username.'));
198
				}
199
				if (!$this->_smtpSend(base64_encode($this->_config['password']), '235')) {
200
					throw new SocketException(__d('cake_dev', 'SMTP server did not accept the password.'));
201
				}
202
			} elseif ($authRequired == '504') {
203
				throw new SocketException(__d('cake_dev', 'SMTP authentication method not allowed, check if SMTP server requires TLS'));
204
			} elseif ($authRequired != '503') {
205
				throw new SocketException(__d('cake_dev', 'SMTP does not require authentication.'));
206
			}
207
		}
208
	}
209
 
210
/**
211
 * Prepares the `MAIL FROM` SMTP command.
212
 *
213
 * @param string $email The email address to send with the command.
214
 * @return string
215
 */
216
	protected function _prepareFromCmd($email) {
217
		return 'MAIL FROM:<' . $email . '>';
218
	}
219
 
220
/**
221
 * Prepares the `RCPT TO` SMTP command.
222
 *
223
 * @param string $email The email address to send with the command.
224
 * @return string
225
 */
226
	protected function _prepareRcptCmd($email) {
227
		return 'RCPT TO:<' . $email . '>';
228
	}
229
 
230
/**
231
 * Prepares the `from` email address.
232
 *
233
 * @return array
234
 */
235
	protected function _prepareFromAddress() {
236
		$from = $this->_cakeEmail->returnPath();
237
		if (empty($from)) {
238
			$from = $this->_cakeEmail->from();
239
		}
240
		return $from;
241
	}
242
 
243
/**
244
 * Prepares the recipient email addresses.
245
 *
246
 * @return array
247
 */
248
	protected function _prepareRecipientAddresses() {
249
		$to = $this->_cakeEmail->to();
250
		$cc = $this->_cakeEmail->cc();
251
		$bcc = $this->_cakeEmail->bcc();
252
		return array_merge(array_keys($to), array_keys($cc), array_keys($bcc));
253
	}
254
 
255
/**
256
 * Prepares the message headers.
257
 *
258
 * @return array
259
 */
260
	protected function _prepareMessageHeaders() {
261
		return $this->_cakeEmail->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'to', 'cc', 'subject'));
262
	}
263
 
264
/**
265
 * Prepares the message body.
266
 *
267
 * @return string
268
 */
269
	protected function _prepareMessage() {
270
		$lines = $this->_cakeEmail->message();
271
		$messages = array();
272
		foreach ($lines as $line) {
273
			if ((!empty($line)) && ($line[0] === '.')) {
274
				$messages[] = '.' . $line;
275
			} else {
276
				$messages[] = $line;
277
			}
278
		}
279
		return implode("\r\n", $messages);
280
	}
281
 
282
/**
283
 * Send emails
284
 *
285
 * @return void
286
 * @throws SocketException
287
 */
288
	protected function _sendRcpt() {
289
		$from = $this->_prepareFromAddress();
290
		$this->_smtpSend($this->_prepareFromCmd(key($from)));
291
 
292
		$emails = $this->_prepareRecipientAddresses();
293
		foreach ($emails as $email) {
294
			$this->_smtpSend($this->_prepareRcptCmd($email));
295
		}
296
	}
297
 
298
/**
299
 * Send Data
300
 *
301
 * @return void
302
 * @throws SocketException
303
 */
304
	protected function _sendData() {
305
		$this->_smtpSend('DATA', '354');
306
 
307
		$headers = $this->_headersToString($this->_prepareMessageHeaders());
308
		$message = $this->_prepareMessage();
309
 
310
		$this->_smtpSend($headers . "\r\n\r\n" . $message . "\r\n\r\n\r\n.");
311
		$this->_content = array('headers' => $headers, 'message' => $message);
312
	}
313
 
314
/**
315
 * Disconnect
316
 *
317
 * @return void
318
 * @throws SocketException
319
 */
320
	protected function _disconnect() {
321
		$this->_smtpSend('QUIT', false);
322
		$this->_socket->disconnect();
323
	}
324
 
325
/**
326
 * Helper method to generate socket
327
 *
328
 * @return void
329
 * @throws SocketException
330
 */
331
	protected function _generateSocket() {
332
		$this->_socket = new CakeSocket($this->_config);
333
	}
334
 
335
/**
336
 * Protected method for sending data to SMTP connection
337
 *
338
 * @param string $data data to be sent to SMTP server
339
 * @param string|bool $checkCode code to check for in server response, false to skip
340
 * @return void
341
 * @throws SocketException
342
 */
343
	protected function _smtpSend($data, $checkCode = '250') {
344
		$this->_lastResponse = array();
345
 
346
		if ($data !== null) {
347
			$this->_socket->write($data . "\r\n");
348
		}
349
		while ($checkCode !== false) {
350
			$response = '';
351
			$startTime = time();
352
			while (substr($response, -2) !== "\r\n" && ((time() - $startTime) < $this->_config['timeout'])) {
353
				$response .= $this->_socket->read();
354
			}
355
			if (substr($response, -2) !== "\r\n") {
356
				throw new SocketException(__d('cake_dev', 'SMTP timeout.'));
357
			}
358
			$responseLines = explode("\r\n", rtrim($response, "\r\n"));
359
			$response = end($responseLines);
360
 
361
			$this->_bufferResponseLines($responseLines);
362
 
363
			if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) {
364
				if ($code[2] === '-') {
365
					continue;
366
				}
367
				return $code[1];
368
			}
369
			throw new SocketException(__d('cake_dev', 'SMTP Error: %s', $response));
370
		}
371
	}
372
 
373
}