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