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
			| 
								 
											2 years ago
										 
									 | 
							
								<?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;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 |