1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13:
14: namespace Jyxo\Mail\Sender;
15:
16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
27: class Smtp
28: {
29: 30: 31: 32: 33:
34: const LINE_END = "\r\n";
35:
36: 37: 38: 39: 40:
41: private $connection = null;
42:
43: 44: 45: 46: 47:
48: private $host = 'localhost';
49:
50: 51: 52: 53: 54:
55: private $port = 25;
56:
57: 58: 59: 60: 61:
62: private $helo = 'localhost';
63:
64: 65: 66: 67: 68:
69: private $timeout = 5;
70:
71: 72: 73: 74: 75: 76: 77: 78:
79: public function __construct($host = 'localhost', $port = 25, $helo = 'localhost', $timeout = 5)
80: {
81: $this->host = (string) $host;
82: $this->port = (int) $port;
83: $this->timeout = (int) $timeout;
84: $this->helo = (string) $helo;
85: }
86:
87: 88: 89:
90: public function __destruct()
91: {
92: if (is_resource($this->connection)) {
93: $this->disconnect();
94: }
95: }
96:
97: 98: 99: 100: 101: 102:
103: public function connect()
104: {
105: $this->connection = fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout);
106: if (false === $this->connection) {
107: throw new SmtpException('CONNECTION: ' . $errno . ' ' . $errstr);
108: }
109:
110:
111: $this->readData();
112:
113:
114: $this->commandHelo();
115:
116: return $this;
117: }
118:
119: 120: 121: 122: 123:
124: public function disconnect()
125: {
126: if (is_resource($this->connection)) {
127: try {
128: $this->reset();
129: $this->writeData('QUIT');
130: fclose($this->connection);
131: $this->connection = null;
132: } catch (\Exception $e) {
133:
134: }
135: }
136:
137: return $this;
138: }
139:
140: 141: 142: 143: 144: 145: 146: 147:
148: public function auth($user, $password)
149: {
150: $this->writeData('AUTH LOGIN');
151: $response = $this->readData();
152: if ('334' !== substr($response, 0, 3)) {
153: throw new SmtpException('AUTH: ' . $response);
154: }
155: $this->writeData(base64_encode($user));
156: $response = $this->readData();
157: if ('334' !== substr($response, 0, 3)) {
158: throw new SmtpException('AUTH: ' . $response);
159: }
160: $this->writeData(base64_encode($password));
161: $response = $this->readData();
162: if ('235' !== substr($response, 0, 3)) {
163: throw new SmtpException('AUTH: ' . $response);
164: }
165:
166: return $this;
167: }
168:
169: 170: 171: 172: 173: 174:
175: public function from($from)
176: {
177: $this->commandMailFrom($from);
178:
179: return $this;
180: }
181:
182: 183: 184: 185: 186: 187:
188: public function recipient($recipient)
189: {
190: $this->commandRcptTo($recipient);
191:
192: return $this;
193: }
194:
195: 196: 197: 198: 199: 200: 201: 202:
203: public function data($header, $body)
204: {
205: $lineEnds = array(\Jyxo\Mail\Sender::LINE_END . '.' => self::LINE_END . '..', \Jyxo\Mail\Sender::LINE_END => self::LINE_END);
206: $header = strtr($header, $lineEnds);
207: $body = strtr($body, $lineEnds);
208: if ('.' == $body[0]) {
209: $body = '.' . $body;
210: }
211:
212: $this->commandData();
213: $this->writeData(trim($header));
214: $this->writeData('');
215: $this->writeData($body);
216: $this->writeData('.');
217:
218: $response = $this->readData();
219: if ('250' !== substr($response, 0, 3)) {
220: throw new SmtpException('SEND: ' . $response);
221: }
222:
223: return $this;
224: }
225:
226: 227: 228: 229: 230:
231: public function reset()
232: {
233: $this->commandRset();
234:
235: return $this;
236: }
237:
238: 239: 240: 241: 242:
243: private function commandHelo()
244: {
245: $this->writeData('EHLO ' . $this->helo);
246: $response = $this->readData();
247: if ('250' !== substr($response, 0, 3)) {
248: $this->writeData('HELO ' . $this->helo);
249: $response = $this->readData();
250: if ('250' !== substr($response, 0, 3)) {
251: throw new SmtpException('HELO: ' . $response);
252: }
253: }
254: }
255:
256: 257: 258: 259: 260: 261:
262: private function commandMailFrom($from)
263: {
264: $this->writeData('MAIL FROM: <' . $from . '>');
265: $response = $this->readData();
266: if ('250' !== substr($response, 0, 3)) {
267: throw new SmtpException('MAIL FROM: ' . $response);
268: }
269: }
270:
271: 272: 273: 274: 275: 276:
277: private function commandRcptTo($recipient)
278: {
279: $this->writeData('RCPT TO: <' . $recipient . '>');
280: $response = $this->readData();
281: if ('250' !== substr($response, 0, 3)) {
282: throw new SmtpException('RCPT TO: ' . $response);
283: }
284: }
285:
286: 287: 288: 289: 290:
291: private function commandData()
292: {
293: $this->writeData('DATA');
294: $response = $this->readData();
295: if ('354' !== substr($response, 0, 3)) {
296: throw new SmtpException('DATA: ' . $response);
297: }
298: }
299:
300: 301: 302: 303: 304:
305: private function commandRset()
306: {
307: $this->writeData('RSET');
308: $response = $this->readData();
309: if ('250' !== substr($response, 0, 3)) {
310: throw new SmtpException('RSET: ' . $response);
311: }
312: }
313:
314: 315: 316: 317: 318:
319: private function readData()
320: {
321: $data = '';
322: $i = 0;
323: while ($line = fgets($this->connection)) {
324: $data .= $line;
325: if (' ' == substr($line, 3, 1)) {
326: break;
327: }
328: }
329: return $data;
330: }
331:
332: 333: 334: 335: 336: 337:
338: private function writeData($data)
339: {
340: if (!fwrite($this->connection, $data . self::LINE_END)) {
341: throw new SmtpException('Error while writing data.');
342: }
343: }
344: }
345: