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