Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/mail/smtp/ |
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/main/lib/mail/smtp/mailer.php |
<?php /** * Bitrix Framework * @package bitrix * @subpackage main * @copyright 2001-2021 Bitrix */ namespace Bitrix\Main\Mail\Smtp; use Bitrix\Main\Error; use Bitrix\Main\HttpApplication; use Bitrix\Main\Mail\Context; use Bitrix\Main\Localization\Loc; use Bitrix\Main\Mail\Sender; use Bitrix\Main\Mail\SenderSendCounter; use PHPMailer\PHPMailer\PHPMailer; use Bitrix\Main\Diag\FileLogger; class Mailer extends PHPMailer { /** * @var Mailer[] $instances */ private static $instances = []; public const KEEP_ALIVE_ALWAYS = 'keep_alive_always'; public const KEEP_ALIVE_NONE = 'keep_alive_none'; public const KEEP_ALIVE_OPTIONAL = 'keep_alive_optional'; private const HEADER_FROM_REGEX = '/(?<=From:).*?(?=\\n)/iu'; private const HEADER_CC_REGEX = '/^\s*cc:(?<emails>.+)/im'; private const HEADER_BCC_REGEX = '/^\s*bcc:(?<emails>.+)/im'; private $configuration; protected function getActualConfiguration(Context $context): array { $configuration = \Bitrix\Main\Config\Configuration::getValue('smtp'); if ($context->getSmtp()) { return [ 'host' => $context->getSmtp()->getHost(), 'port' => $context->getSmtp()->getPort(), 'encryption_type' => $context->getSmtp()->getProtocol() ?? 'smtp', 'login' => $context->getSmtp()->getLogin(), 'password' => $context->getSmtp()->getPassword(), 'from' => $context->getSmtp()->getFrom(), 'debug' => $configuration['debug'] ?? false, 'force_from' => $configuration['force_from'] ?? false, 'logFile' => $configuration['log_file'] ?? null, 'isOauth' => $context->getSmtp()->getIsOauth(), ]; } return $configuration ?? []; } /** * Prepare PHPMailer configuration from bitrix/.settings.php * @param array|null $configuration * @return bool */ public function prepareConfiguration(Context $context): bool { $this->configuration = $this->getActualConfiguration($context); $this->SMTPDebug = $this->configuration['debug'] ?? false; if ($this->SMTPDebug) { $configuration = $this->configuration; $this->Debugoutput = function ($logMessage) use ($configuration) { $logger = new FileLogger( $configuration['logFile'] ?? (($_SERVER['DOCUMENT_ROOT'] ?? __DIR__) . '/mailer.log') ); $logger->info($logMessage); }; } $this->isSMTP(); $this->SMTPAuth = (bool)$this->configuration['password']; $this->prepareOauthConfiguration(); if ( !$this->configuration['host'] || !$this->configuration['login'] ) { return false; } $this->From = $this->configuration['from']; $this->Host = $this->configuration['host']; $this->Username = $this->configuration['login']; $this->Password = $this->configuration['password'] ?? ''; $this->Port = $this->configuration['port'] ?? 465; if ( 'smtps' === $this->configuration['encryption_type'] || ('smtp' !== $this->configuration['encryption_type'] && 465 === $this->port)) { $this->SMTPSecure = $this->Port == 465 ? PHPMailer::ENCRYPTION_SMTPS : PHPMailer::ENCRYPTION_STARTTLS; } $this->Timeout = $this->configuration['connection_timeout'] ?? 30; return true; } /** * Prepares OAuth configuration, if need * * @return void */ private function prepareOauthConfiguration(): void { if (!empty($this->configuration['isOauth'])) { $this->AuthType = 'XOAUTH2'; $this->setOAuth(new PreparedOauthCredentials([ 'userName' => $this->configuration['login'], 'token' => $this->configuration['password'], ])); } } /** * Set prepared MIME body data * @param $body */ public function setMIMEBody($body) { $this->MIMEBody = $body; } /** * Set prepared MIME header * @param $headers */ public function setMIMEHeader($headers) { $this->MIMEHeader = $headers; } /** * Send mail via smtp connection * @param string $to * @param string $subject * @param string $message * @param string $additional_headers * @param string $additional_parameters * @return bool * @throws \PHPMailer\PHPMailer\Exception */ public function sendMailBySmtp( string $sourceTo, string $subject, string $message, string $additional_headers, string $additional_parameters ): bool { $addresses = Mailer::parseAddresses($sourceTo)??[]; if (empty($addresses)) { return false; } $eol = \Bitrix\Main\Mail\Mail::getMailEol(); if ($subject && $additional_headers) { $this->Subject = $subject; $additional_headers .= $eol . 'Subject: ' . $subject; } preg_match(self::HEADER_FROM_REGEX, $additional_headers, $headerFrom);; if ($this->configuration['force_from'] && $headerFrom) { $additional_headers = preg_replace( self::HEADER_FROM_REGEX, $this->configuration['from'], $additional_headers ); } if (!$headerFrom) { $additional_headers .= $eol . 'From: ' . $this->configuration['from']; } $this->clearAllRecipients(); foreach ($addresses as $to) { if (!$to['address']) { continue; } $this->addAddress($to['address'], $to['name']); } $additional_headers .= $eol . 'To: ' . $sourceTo; $this->prepareBCCRecipients($additional_headers); $this->prepareCCRecipients($additional_headers); $this->setMIMEBody($message); $this->setMIMEHeader($additional_headers); $canSend = $this->checkLimit(); if (!$canSend) { return false; } $sendResult = $this->postSend(); if ($sendResult) { $this->increaseLimit(); } return $sendResult; } private function prepareCCRecipients($additional_headers) { preg_match(self::HEADER_CC_REGEX, $additional_headers, $matches); if ($matches) { $recipients = explode(',', trim($matches['emails'])); foreach ($recipients as $to) { $to = self::parseAddresses($to) ?? []; if (!$to) { continue; } $this->addCC($to[0]['address'], $to[0]['name']); } } } private function prepareBCCRecipients($additional_headers) { preg_match(self::HEADER_BCC_REGEX, $additional_headers, $matches); if ($matches) { $recipients = explode(',', trim($matches['emails'])); foreach ($recipients as $to) { $to = self::parseAddresses($to) ?? []; if (!$to) { continue; } $this->addBCC($to[0]['address'], $to[0]['name']); } } } /** * Returns instance of current class * @param Context $context * @return Mailer|null * @throws \Bitrix\Main\ArgumentException * @throws \PHPMailer\PHPMailer\Exception */ public static function getInstance(Context $context): ?Mailer { $key = hash('sha256', serialize($context)); if (!static::$instances[$key]) { $mail = new Mailer(); if (!$mail->prepareConfiguration($context)) { return null; } if ($context->getSmtp()) { $mail->setFrom($context->getSmtp()->getFrom()); } switch ($context->getKeepAlive()) { default: case self::KEEP_ALIVE_NONE: $mail->SMTPKeepAlive = false; break; case self::KEEP_ALIVE_ALWAYS: $mail->SMTPKeepAlive = true; HttpApplication::getInstance()->addBackgroundJob( function () use ($mail) { $mail->smtpClose(); }); break; case self::KEEP_ALIVE_OPTIONAL: $mail->SMTPKeepAlive = true; break; } static::$instances[$key] = $mail; } return static::$instances[$key]; } /** * Returns true if Login, Password and Server parameters is right * Returns false if any errors in connection was detected * @param Context $context * @param \Bitrix\Main\ErrorCollection $errors * @return bool * @throws \PHPMailer\PHPMailer\Exception */ public static function checkConnect(Context $context, \Bitrix\Main\ErrorCollection $errors): bool { $mail = new Mailer(); if (\Bitrix\Main\ModuleManager::isModuleInstalled('bitrix24')) { // Private addresses can't be used in the cloud $ip = \Bitrix\Main\Web\IpAddress::createByName($context->getSmtp()->getHost()); if ($ip->isPrivate()) { $errors->setError(new Error('SMTP server address is invalid')); return false; } } if (!$mail->prepareConfiguration($context)) { return false; } if ($mail->smtpConnect()) { $mail->smtpClose(); return true; } $errors->setError(new Error(Loc::getMessage('main_mail_smtp_connection_failed'))); return false; } private function checkLimit(): bool { $from = self::parseAddresses($this->From)[0]['address']; $count = count($this->getAllRecipientAddresses()); $emailCounter = new SenderSendCounter(); $emailDailyLimit = Sender::getEmailLimit($from); if($emailDailyLimit && ($emailCounter->get($from) + $count) > $emailDailyLimit) { //daily limit exceeded return false; } return true; } private function increaseLimit() { $from = self::parseAddresses($this->From)[0]['address']; $emailDailyLimit = Sender::getEmailLimit($from); if (!$emailDailyLimit) { return; } $emailCounter = new SenderSendCounter(); $count = count($this->getAllRecipientAddresses()); $emailCounter->increment($from, $count); } /** * Closes all instances connections */ public static function closeConnections(): void { foreach (static::$instances as $instance) { $instance->smtpClose(); } } }