A super easy PHP Framework for web development!
				https://github.com/exacti/phacil-framework
			
			
		
			You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							338 lines
						
					
					
						
							7.7 KiB
						
					
					
				
			
		
		
	
	
							338 lines
						
					
					
						
							7.7 KiB
						
					
					
				<?php
 | 
						|
/*
 | 
						|
 * Copyright © 2024 ExacTI Technology Solutions. All rights reserved.
 | 
						|
 * GPLv3 General License.
 | 
						|
 * https://exacti.com.br
 | 
						|
 * Phacil PHP Framework - https://github.com/exacti/phacil-framework
 | 
						|
 */
 | 
						|
 | 
						|
namespace Phacil\Framework\Mail\Drivers;
 | 
						|
 | 
						|
class SMTP implements \Phacil\Framework\Mail\Api\DriverInterface {
 | 
						|
	/**
 | 
						|
	 * 
 | 
						|
	 * @var string
 | 
						|
	 */
 | 
						|
	protected $hostname;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * 
 | 
						|
	 * @var string
 | 
						|
	 */
 | 
						|
	protected $username;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * 
 | 
						|
	 * @var string
 | 
						|
	 */
 | 
						|
	protected $password;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * 
 | 
						|
	 * @var int
 | 
						|
	 */
 | 
						|
	public $port = 25;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * 
 | 
						|
	 * @var int
 | 
						|
	 */
 | 
						|
	public $timeout = 5;
 | 
						|
 | 
						|
	private $crlf = self::CRLF;
 | 
						|
 | 
						|
	private $newline = self::NEWLINE;
 | 
						|
 | 
						|
	private $from;
 | 
						|
 | 
						|
	private $verp;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * 
 | 
						|
	 * @var \Phacil\Framework\Mail\Api\MailInterface
 | 
						|
	 */
 | 
						|
	private $mail;
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param \Phacil\Framework\Mail\Api\MailInterface $mail 
 | 
						|
	 * @return void 
 | 
						|
	 */
 | 
						|
	public function __construct(\Phacil\Framework\Mail\Api\MailInterface $mail){
 | 
						|
		$this->mail = $mail;
 | 
						|
	}
 | 
						|
 | 
						|
	protected function validate() {
 | 
						|
		$this->verp = $this->mail->getVerp();
 | 
						|
		$this->from = $this->mail->getFrom();
 | 
						|
		$this->port = $this->mail->getConfig()->get(self::CONFIG_SMTP_PORT) ?: $this->port;
 | 
						|
		$this->hostname = $this->mail->getConfig()->get(self::CONFIG_SMTP_HOSTNAME);
 | 
						|
		$this->username = $this->mail->getConfig()->get(self::CONFIG_SMTP_USERNAME);
 | 
						|
		$this->password = $this->mail->getConfig()->get(self::CONFIG_SMTP_PASSWORD);
 | 
						|
		$this->timeout = $this->mail->getConfig()->get(self::CONFIG_SMTP_TIMEOUT) ?: $this->timeout;
 | 
						|
 | 
						|
		if(!$this->hostname || !is_string($this->hostname)) 
 | 
						|
			throw new \Phacil\Framework\Exception\InvalidArgumentException('STMP requires a valid hostname configuration');
 | 
						|
		
 | 
						|
		if(!empty($this->username) && !is_string($this->username)) 
 | 
						|
			throw new \Phacil\Framework\Exception\InvalidArgumentException('STMP requires a valid smtp username configuration');
 | 
						|
		
 | 
						|
		if(!empty($this->password) && !is_string($this->password)) 
 | 
						|
			throw new \Phacil\Framework\Exception\InvalidArgumentException('STMP requires a valid smtp password configuration');
 | 
						|
		
 | 
						|
		if(!$this->port || !is_int($this->port)) 
 | 
						|
			throw new \Phacil\Framework\Exception\InvalidArgumentException('STMP requires a valid smtp port configuration');
 | 
						|
		
 | 
						|
		if(!$this->timeout || !is_int($this->timeout)) 
 | 
						|
			throw new \Phacil\Framework\Exception\InvalidArgumentException('STMP requires a valid timeout configuration');
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * {@inheritdoc}
 | 
						|
	 */
 | 
						|
	public function send($to, $message, $header){
 | 
						|
		$this->validate();
 | 
						|
 | 
						|
		$handle = fsockopen($this->hostname, $this->port, $errno, $errstr, $this->timeout);
 | 
						|
 | 
						|
		if (!$handle) {
 | 
						|
			throw new \Phacil\Framework\Exception('Error: ' . $errstr . ' (' . $errno . ')');
 | 
						|
		} else {
 | 
						|
			if (substr(PHP_OS, 0, 3) != 'WIN') {
 | 
						|
				socket_set_timeout($handle, $this->timeout, 0);
 | 
						|
			}
 | 
						|
 | 
						|
			while ($line = fgets($handle, 515)) {
 | 
						|
				if (substr($line, 3, 1) == ' ') {
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if (substr($this->hostname, 0, 3) == 'tls') {
 | 
						|
				fputs($handle, 'STARTTLS' . $this->crlf);
 | 
						|
				$reply = '';
 | 
						|
 | 
						|
				while ($line = fgets($handle, 515)) {
 | 
						|
					$reply .= $line;
 | 
						|
 | 
						|
					if (substr($line, 3, 1) == ' ') {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (substr($reply, 0, 3) != 220) {
 | 
						|
					throw new \Phacil\Framework\Exception('Error: STARTTLS not accepted from server!');
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if (!empty($this->username)  && !empty($this->password)) {
 | 
						|
				fputs($handle, 'EHLO ' . getenv('SERVER_NAME') . $this->crlf);
 | 
						|
 | 
						|
				$reply = '';
 | 
						|
 | 
						|
				while ($line = fgets($handle, 515)) {
 | 
						|
					$reply .= $line;
 | 
						|
 | 
						|
					if (substr($line, 3, 1) == ' ') {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (substr($reply, 0, 3) != 250) {
 | 
						|
					throw new \Phacil\Framework\Exception('Error: EHLO not accepted from server!');
 | 
						|
				}
 | 
						|
 | 
						|
				fputs($handle, 'AUTH LOGIN' . $this->crlf);
 | 
						|
 | 
						|
				$reply = '';
 | 
						|
 | 
						|
				while ($line = fgets($handle, 515)) {
 | 
						|
					$reply .= $line;
 | 
						|
 | 
						|
					if (substr($line, 3, 1) == ' ') {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (substr($reply, 0, 3) != 334) {
 | 
						|
					throw new \Phacil\Framework\Exception('Error: AUTH LOGIN not accepted from server!');
 | 
						|
				}
 | 
						|
 | 
						|
				fputs($handle, base64_encode($this->username) . $this->crlf);
 | 
						|
 | 
						|
				$reply = '';
 | 
						|
 | 
						|
				while ($line = fgets($handle, 515)) {
 | 
						|
					$reply .= $line;
 | 
						|
 | 
						|
					if (substr($line, 3, 1) == ' ') {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (substr($reply, 0, 3) != 334) {
 | 
						|
					throw new \Phacil\Framework\Exception('Error: Username not accepted from server!');
 | 
						|
				}
 | 
						|
 | 
						|
				fputs($handle, base64_encode($this->password) . $this->crlf);
 | 
						|
 | 
						|
				$reply = '';
 | 
						|
 | 
						|
				while ($line = fgets($handle, 515)) {
 | 
						|
					$reply .= $line;
 | 
						|
 | 
						|
					if (substr($line, 3, 1) == ' ') {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (substr($reply, 0, 3) != 235) {
 | 
						|
					throw new \Phacil\Framework\Exception('Error: Password not accepted from server!');
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				fputs($handle, 'HELO ' . getenv('SERVER_NAME') . $this->crlf);
 | 
						|
 | 
						|
				$reply = '';
 | 
						|
 | 
						|
				while ($line = fgets($handle, 515)) {
 | 
						|
					$reply .= $line;
 | 
						|
 | 
						|
					if (substr($line, 3, 1) == ' ') {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if (substr($reply, 0, 3) != 250) {
 | 
						|
					throw new \Phacil\Framework\Exception('Error: HELO not accepted from server!');
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if ($this->verp) {
 | 
						|
				fputs($handle, 'MAIL FROM: <' . $this->from . '>XVERP' . $this->crlf);
 | 
						|
			} else {
 | 
						|
				fputs($handle, 'MAIL FROM: <' . $this->from . '>' . $this->crlf);
 | 
						|
			}
 | 
						|
 | 
						|
			$reply = '';
 | 
						|
 | 
						|
			while ($line = fgets($handle, 515)) {
 | 
						|
				$reply .= $line;
 | 
						|
 | 
						|
				if (substr($line, 3, 1) == ' ') {
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if (substr($reply, 0, 3) != 250) {
 | 
						|
				throw new \Phacil\Framework\Exception('Error: MAIL FROM not accepted from server!');
 | 
						|
			}
 | 
						|
 | 
						|
			if (!is_array($to)) {
 | 
						|
				fputs($handle, 'RCPT TO: <' . $to . '>' . $this->crlf);
 | 
						|
 | 
						|
				$reply = '';
 | 
						|
 | 
						|
				while ($line = fgets($handle, 515)) {
 | 
						|
					$reply .= $line;
 | 
						|
 | 
						|
					if (substr($line, 3, 1) == ' ') {
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) {
 | 
						|
					throw new \Phacil\Framework\Exception('Error: RCPT TO not accepted from server!');
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				foreach ($to as $recipient) {
 | 
						|
					fputs($handle, 'RCPT TO: <' . $recipient . '>' . $this->crlf);
 | 
						|
 | 
						|
					$reply = '';
 | 
						|
 | 
						|
					while ($line = fgets($handle, 515)) {
 | 
						|
						$reply .= $line;
 | 
						|
 | 
						|
						if (substr($line, 3, 1) == ' ') {
 | 
						|
							break;
 | 
						|
						}
 | 
						|
					}
 | 
						|
 | 
						|
					if ((substr($reply, 0, 3) != 250) && (substr($reply, 0, 3) != 251)) {
 | 
						|
						throw new \Phacil\Framework\Exception('Error: RCPT TO not accepted from server!');
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			fputs($handle, 'DATA' . $this->crlf);
 | 
						|
 | 
						|
			$reply = '';
 | 
						|
 | 
						|
			while ($line = fgets($handle, 515)) {
 | 
						|
				$reply .= $line;
 | 
						|
 | 
						|
				if (substr($line, 3, 1) == ' ') {
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if (substr($reply, 0, 3) != 354) {
 | 
						|
				throw new \Phacil\Framework\Exception('Error: DATA not accepted from server!');
 | 
						|
			}
 | 
						|
 | 
						|
			// According to rfc 821 we should not send more than 1000 including the CRLF
 | 
						|
			$message = str_replace("\r\n", "\n",  $header . $message);
 | 
						|
			$message = str_replace("\r", "\n", $message);
 | 
						|
 | 
						|
			$lines = explode("\n", $message);
 | 
						|
 | 
						|
			foreach ($lines as $line) {
 | 
						|
				$results = str_split($line, 998);
 | 
						|
 | 
						|
				foreach ($results as $result) {
 | 
						|
					if (substr(PHP_OS, 0, 3) != 'WIN') {
 | 
						|
						fputs($handle, $result . $this->crlf);
 | 
						|
					} else {
 | 
						|
						fputs($handle, str_replace("\n", "\r\n", $result) . $this->crlf);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			fputs($handle, '.' . $this->crlf);
 | 
						|
 | 
						|
			$reply = '';
 | 
						|
 | 
						|
			while ($line = fgets($handle, 515)) {
 | 
						|
				$reply .= $line;
 | 
						|
 | 
						|
				if (substr($line, 3, 1) == ' ') {
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if (substr($reply, 0, 3) != 250) {
 | 
						|
				throw new \Phacil\Framework\Exception('Error: DATA not accepted from server!');
 | 
						|
			}
 | 
						|
 | 
						|
			fputs($handle, 'QUIT' . $this->crlf);
 | 
						|
 | 
						|
			$reply = '';
 | 
						|
 | 
						|
			while ($line = fgets($handle, 515)) {
 | 
						|
				$reply .= $line;
 | 
						|
 | 
						|
				if (substr($line, 3, 1) == ' ') {
 | 
						|
					break;
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			if (substr($reply, 0, 3) != 221) {
 | 
						|
				throw new \Phacil\Framework\Exception('Error: QUIT not accepted from server!');
 | 
						|
			}
 | 
						|
 | 
						|
			fclose($handle);
 | 
						|
 | 
						|
			return true;
 | 
						|
		}
 | 
						|
	
 | 
						|
	}
 | 
						|
 | 
						|
} |