diff options
Diffstat (limited to 'build/integration/features/bootstrap/FakeSMTPHelper.php')
-rw-r--r-- | build/integration/features/bootstrap/FakeSMTPHelper.php | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/build/integration/features/bootstrap/FakeSMTPHelper.php b/build/integration/features/bootstrap/FakeSMTPHelper.php new file mode 100644 index 00000000000..9a64b6d31de --- /dev/null +++ b/build/integration/features/bootstrap/FakeSMTPHelper.php @@ -0,0 +1,159 @@ +<?php + +// Code below modified from https://github.com/axllent/fake-smtp/blob/f0856f8a0df6f4ca5a573cf31428c09ebc5b9ea3/fakeSMTP.php, +// which is under the MIT license (https://github.com/axllent/fake-smtp/blob/f0856f8a0df6f4ca5a573cf31428c09ebc5b9ea3/LICENSE) + +/** + * fakeSMTP - A PHP / inetd fake smtp server. + * Allows client<->server interaction + * The comunication is based upon the SMPT standards defined in http://www.lesnikowski.com/mail/Rfc/rfc2821.txt + */ + +class fakeSMTP { + public $logFile = false; + public $serverHello = 'fakeSMTP ESMTP PHP Mail Server Ready'; + + public function __construct($fd) { + $this->mail = []; + $this->mail['ipaddress'] = false; + $this->mail['emailSender'] = ''; + $this->mail['emailRecipients'] = []; + $this->mail['emailSubject'] = false; + $this->mail['rawEmail'] = false; + $this->mail['emailHeaders'] = false; + $this->mail['emailBody'] = false; + + $this->fd = $fd; + } + + public function receive() { + $hasValidFrom = false; + $hasValidTo = false; + $receivingData = false; + $header = true; + $this->reply('220 '.$this->serverHello); + $this->mail['ipaddress'] = $this->detectIP(); + while ($data = fgets($this->fd)) { + $data = preg_replace('@\r\n@', "\n", $data); + + if (!$receivingData) { + $this->log($data); + } + + if (!$receivingData && preg_match('/^MAIL FROM:\s?<(.*)>/i', $data, $match)) { + if (preg_match('/(.*)@\[.*\]/i', $match[1]) || $match[1] != '' || $this->validateEmail($match[1])) { + $this->mail['emailSender'] = $match[1]; + $this->reply('250 2.1.0 Ok'); + $hasValidFrom = true; + } else { + $this->reply('551 5.1.7 Bad sender address syntax'); + } + } elseif (!$receivingData && preg_match('/^RCPT TO:\s?<(.*)>/i', $data, $match)) { + if (!$hasValidFrom) { + $this->reply('503 5.5.1 Error: need MAIL command'); + } else { + if (preg_match('/postmaster@\[.*\]/i', $match[1]) || $this->validateEmail($match[1])) { + array_push($this->mail['emailRecipients'], $match[1]); + $this->reply('250 2.1.5 Ok'); + $hasValidTo = true; + } else { + $this->reply('501 5.1.3 Bad recipient address syntax '.$match[1]); + } + } + } elseif (!$receivingData && preg_match('/^RSET$/i', trim($data))) { + $this->reply('250 2.0.0 Ok'); + $hasValidFrom = false; + $hasValidTo = false; + } elseif (!$receivingData && preg_match('/^NOOP$/i', trim($data))) { + $this->reply('250 2.0.0 Ok'); + } elseif (!$receivingData && preg_match('/^VRFY (.*)/i', trim($data), $match)) { + $this->reply('250 2.0.0 '.$match[1]); + } elseif (!$receivingData && preg_match('/^DATA/i', trim($data))) { + if (!$hasValidTo) { + $this->reply('503 5.5.1 Error: need RCPT command'); + } else { + $this->reply('354 Ok Send data ending with <CRLF>.<CRLF>'); + $receivingData = true; + } + } elseif (!$receivingData && preg_match('/^(HELO|EHLO)/i', $data)) { + $this->reply('250 HELO '.$this->mail['ipaddress']); + } elseif (!$receivingData && preg_match('/^QUIT/i', trim($data))) { + break; + } elseif (!$receivingData) { + //~ $this->reply('250 Ok'); + $this->reply('502 5.5.2 Error: command not recognized'); + } elseif ($receivingData && $data == ".\n") { + /* Email Received, now let's look at it */ + $receivingData = false; + $this->reply('250 2.0.0 Ok: queued as '.$this->generateRandom(10)); + $splitmail = explode("\n\n", $this->mail['rawEmail'], 2); + if (count($splitmail) == 2) { + $this->mail['emailHeaders'] = $splitmail[0]; + $this->mail['emailBody'] = $splitmail[1]; + $headers = preg_replace("/ \s+/", ' ', preg_replace("/\n\s/", ' ', $this->mail['emailHeaders'])); + $headerlines = explode("\n", $headers); + for ($i=0; $i<count($headerlines); $i++) { + if (preg_match('/^Subject: (.*)/i', $headerlines[$i], $matches)) { + $this->mail['emailSubject'] = trim($matches[1]); + } + } + } else { + $this->mail['emailBody'] = $splitmail[0]; + } + set_time_limit(5); // Just run the exit to prevent open threads / abuse + } elseif ($receivingData) { + $this->mail['rawEmail'] .= $data; + } + } + /* Say good bye */ + $this->reply('221 2.0.0 Bye '.$this->mail['ipaddress']); + + fclose($this->fd); + } + + public function log($s) { + if ($this->logFile) { + file_put_contents($this->logFile, trim($s)."\n", FILE_APPEND); + } + } + + private function reply($s) { + $this->log("REPLY:$s"); + fwrite($this->fd, $s . "\r\n"); + } + + private function detectIP() { + $raw = explode(':', stream_socket_get_name($this->fd, true)); + return $raw[0]; + } + + private function validateEmail($email) { + return preg_match('/^[_a-z0-9-+]+(\.[_a-z0-9-+]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})$/', strtolower($email)); + } + + private function generateRandom($length=8) { + $password = ''; + $possible = '2346789BCDFGHJKLMNPQRTVWXYZ'; + $maxlength = strlen($possible); + $i = 0; + for ($i=0; $i < $length; $i++) { + $char = substr($possible, mt_rand(0, $maxlength-1), 1); + if (!strstr($password, $char)) { + $password .= $char; + } + } + return $password; + } +} + +$socket = stream_socket_server('tcp://127.0.0.1:2525', $errno, $errstr); +if (!$socket) { + exit(); +} + +while ($fd = stream_socket_accept($socket)) { + $fakeSMTP = new fakeSMTP($fd); + $fakeSMTP->receive(); +} + +fclose($socket); |