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