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