1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20:
21: App::uses('CakeSocket', 'Network');
22:
23: 24: 25: 26: 27:
28: class SmtpTransport extends AbstractTransport {
29:
30: 31: 32: 33: 34:
35: protected $_socket;
36:
37: 38: 39: 40: 41:
42: protected $_cakeEmail;
43:
44: 45: 46: 47: 48:
49: protected $_content;
50:
51: 52: 53: 54: 55: 56: 57:
58: public function send(CakeEmail $email) {
59: $this->_cakeEmail = $email;
60:
61: $this->_connect();
62: $this->_auth();
63: $this->_sendRcpt();
64: $this->_sendData();
65: $this->_disconnect();
66:
67: return $this->_content;
68: }
69:
70: 71: 72: 73: 74: 75:
76: public function config($config = null) {
77: if ($config === null) {
78: return $this->_config;
79: }
80: $default = array(
81: 'host' => 'localhost',
82: 'port' => 25,
83: 'timeout' => 30,
84: 'username' => null,
85: 'password' => null,
86: 'client' => null,
87: 'tls' => false
88: );
89: $this->_config = array_merge($default, $this->_config, $config);
90: return $this->_config;
91: }
92:
93: 94: 95: 96: 97: 98:
99: protected function _connect() {
100: $this->_generateSocket();
101: if (!$this->_socket->connect()) {
102: throw new SocketException(__d('cake_dev', 'Unable to connect to SMTP server.'));
103: }
104: $this->_smtpSend(null, '220');
105:
106: if (isset($this->_config['client'])) {
107: $host = $this->_config['client'];
108: } elseif ($httpHost = env('HTTP_HOST')) {
109: list($host) = explode(':', $httpHost);
110: } else {
111: $host = 'localhost';
112: }
113:
114: try {
115: $this->_smtpSend("EHLO {$host}", '250');
116: if ($this->_config['tls']) {
117: $this->_smtpSend("STARTTLS", '220');
118: $this->_socket->enableCrypto('tls');
119: $this->_smtpSend("EHLO {$host}", '250');
120: }
121: } catch (SocketException $e) {
122: if ($this->_config['tls']) {
123: throw new SocketException(__d('cake_dev', 'SMTP server did not accept the connection or trying to connect to non TLS SMTP server using TLS.'));
124: }
125: try {
126: $this->_smtpSend("HELO {$host}", '250');
127: } catch (SocketException $e2) {
128: throw new SocketException(__d('cake_dev', 'SMTP server did not accept the connection.'));
129: }
130: }
131: }
132:
133: 134: 135: 136: 137: 138:
139: protected function _auth() {
140: if (isset($this->_config['username']) && isset($this->_config['password'])) {
141: $authRequired = $this->_smtpSend('AUTH LOGIN', '334|503');
142: if ($authRequired == '334') {
143: if (!$this->_smtpSend(base64_encode($this->_config['username']), '334')) {
144: throw new SocketException(__d('cake_dev', 'SMTP server did not accept the username.'));
145: }
146: if (!$this->_smtpSend(base64_encode($this->_config['password']), '235')) {
147: throw new SocketException(__d('cake_dev', 'SMTP server did not accept the password.'));
148: }
149: } elseif ($authRequired == '504') {
150: throw new SocketException(__d('cake_dev', 'SMTP authentication method not allowed, check if SMTP server requires TLS'));
151: } elseif ($authRequired != '503') {
152: throw new SocketException(__d('cake_dev', 'SMTP does not require authentication.'));
153: }
154: }
155: }
156:
157: 158: 159: 160: 161: 162:
163: protected function _sendRcpt() {
164: $from = $this->_cakeEmail->returnPath();
165: if (empty($from)) {
166: $from = $this->_cakeEmail->from();
167: }
168: $this->_smtpSend('MAIL FROM:<' . key($from) . '>');
169:
170: $to = $this->_cakeEmail->to();
171: $cc = $this->_cakeEmail->cc();
172: $bcc = $this->_cakeEmail->bcc();
173: $emails = array_merge(array_keys($to), array_keys($cc), array_keys($bcc));
174: foreach ($emails as $email) {
175: $this->_smtpSend('RCPT TO:<' . $email . '>');
176: }
177: }
178:
179: 180: 181: 182: 183: 184:
185: protected function _sendData() {
186: $this->_smtpSend('DATA', '354');
187:
188: $headers = $this->_cakeEmail->getHeaders(array('from', 'sender', 'replyTo', 'readReceipt', 'to', 'cc', 'subject'));
189: $headers = $this->_headersToString($headers);
190: $lines = $this->_cakeEmail->message();
191: $messages = array();
192: foreach ($lines as $line) {
193: if ((!empty($line)) && ($line[0] === '.')) {
194: $messages[] = '.' . $line;
195: } else {
196: $messages[] = $line;
197: }
198: }
199: $message = implode("\r\n", $messages);
200: $this->_smtpSend($headers . "\r\n\r\n" . $message . "\r\n\r\n\r\n.");
201: $this->_content = array('headers' => $headers, 'message' => $message);
202: }
203:
204: 205: 206: 207: 208: 209:
210: protected function _disconnect() {
211: $this->_smtpSend('QUIT', false);
212: $this->_socket->disconnect();
213: }
214:
215: 216: 217: 218: 219: 220:
221: protected function _generateSocket() {
222: $this->_socket = new CakeSocket($this->_config);
223: }
224:
225: 226: 227: 228: 229: 230: 231: 232:
233: protected function _smtpSend($data, $checkCode = '250') {
234: if ($data !== null) {
235: $this->_socket->write($data . "\r\n");
236: }
237: while ($checkCode !== false) {
238: $response = '';
239: $startTime = time();
240: while (substr($response, -2) !== "\r\n" && ((time() - $startTime) < $this->_config['timeout'])) {
241: $response .= $this->_socket->read();
242: }
243: if (substr($response, -2) !== "\r\n") {
244: throw new SocketException(__d('cake_dev', 'SMTP timeout.'));
245: }
246: $responseLines = explode("\r\n", rtrim($response, "\r\n"));
247: $response = end($responseLines);
248:
249: if (preg_match('/^(' . $checkCode . ')(.)/', $response, $code)) {
250: if ($code[2] === '-') {
251: continue;
252: }
253: return $code[1];
254: }
255: throw new SocketException(__d('cake_dev', 'SMTP Error: %s', $response));
256: }
257: }
258:
259: }
260: