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