1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Mail;
9:
10: use Nette;
11:
12:
13: 14: 15:
16: class SmtpMailer extends Nette\Object implements IMailer
17: {
18:
19: private $connection;
20:
21:
22: private $host;
23:
24:
25: private $port;
26:
27:
28: private $username;
29:
30:
31: private $password;
32:
33:
34: private $secure;
35:
36:
37: private $timeout;
38:
39:
40: private $persistent;
41:
42:
43: public function __construct(array $options = array())
44: {
45: if (isset($options['host'])) {
46: $this->host = $options['host'];
47: $this->port = isset($options['port']) ? (int) $options['port'] : NULL;
48: } else {
49: $this->host = ini_get('SMTP');
50: $this->port = (int) ini_get('smtp_port');
51: }
52: $this->username = isset($options['username']) ? $options['username'] : '';
53: $this->password = isset($options['password']) ? $options['password'] : '';
54: $this->secure = isset($options['secure']) ? $options['secure'] : '';
55: $this->timeout = isset($options['timeout']) ? (int) $options['timeout'] : 20;
56: if (!$this->port) {
57: $this->port = $this->secure === 'ssl' ? 465 : 25;
58: }
59: $this->persistent = !empty($options['persistent']);
60: }
61:
62:
63: 64: 65: 66: 67:
68: public function send(Message $mail)
69: {
70: $mail = clone $mail;
71:
72: try {
73: if (!$this->connection) {
74: $this->connect();
75: }
76:
77: if (($from = $mail->getHeader('Return-Path'))
78: || ($from = key($mail->getHeader('From')))
79: ) {
80: $this->write("MAIL FROM:<$from>", 250);
81: }
82:
83: foreach (array_merge(
84: (array) $mail->getHeader('To'),
85: (array) $mail->getHeader('Cc'),
86: (array) $mail->getHeader('Bcc')
87: ) as $email => $name) {
88: $this->write("RCPT TO:<$email>", array(250, 251));
89: }
90:
91: $mail->setHeader('Bcc', NULL);
92: $data = $mail->generateMessage();
93: $this->write('DATA', 354);
94: $data = preg_replace('#^\.#m', '..', $data);
95: $this->write($data);
96: $this->write('.', 250);
97:
98: if (!$this->persistent) {
99: $this->write('QUIT', 221);
100: $this->disconnect();
101: }
102: } catch (SmtpException $e) {
103: if ($this->connection) {
104: $this->disconnect();
105: }
106: throw $e;
107: }
108: }
109:
110:
111: 112: 113: 114:
115: protected function connect()
116: {
117: $this->connection = @stream_socket_client(
118: ($this->secure === 'ssl' ? 'ssl://' : '') . $this->host . ':' . $this->port,
119: $errno, $error, $this->timeout
120: );
121: if (!$this->connection) {
122: throw new SmtpException($error, $errno);
123: }
124: stream_set_timeout($this->connection, $this->timeout, 0);
125: $this->read();
126:
127: $self = isset($_SERVER['HTTP_HOST']) && preg_match('#^[\w.-]+\z#', $_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : 'localhost';
128: $this->write("EHLO $self");
129: if ((int) $this->read() !== 250) {
130: $this->write("HELO $self", 250);
131: }
132:
133: if ($this->secure === 'tls') {
134: $this->write('STARTTLS', 220);
135: if (!stream_socket_enable_crypto($this->connection, TRUE, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
136: throw new SmtpException('Unable to connect via TLS.');
137: }
138: $this->write("EHLO $self", 250);
139: }
140:
141: if ($this->username != NULL && $this->password != NULL) {
142: $this->write('AUTH LOGIN', 334);
143: $this->write(base64_encode($this->username), 334, 'username');
144: $this->write(base64_encode($this->password), 235, 'password');
145: }
146: }
147:
148:
149: 150: 151: 152:
153: protected function disconnect()
154: {
155: fclose($this->connection);
156: $this->connection = NULL;
157: }
158:
159:
160: 161: 162: 163: 164: 165: 166:
167: protected function write($line, $expectedCode = NULL, $message = NULL)
168: {
169: fwrite($this->connection, $line . Message::EOL);
170: if ($expectedCode) {
171: $response = $this->read();
172: if (!in_array((int) $response, (array) $expectedCode, TRUE)) {
173: throw new SmtpException('SMTP server did not accept ' . ($message ? $message : $line) . ' with error: ' . trim($response));
174: }
175: }
176: }
177:
178:
179: 180: 181: 182:
183: protected function read()
184: {
185: $s = '';
186: while (($line = fgets($this->connection, 1e3)) != NULL) {
187: $s .= $line;
188: if (substr($line, 3, 1) === ' ') {
189: break;
190: }
191: }
192: return $s;
193: }
194:
195: }
196: