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